Что означает void * и как его использовать?

Сегодня, когда я читал код других, я видел что-то вроде void *func(void* i);, что означает это void* здесь для имени функции и для типа переменной, соответственно?

Кроме того, когда нам нужно использовать этот указатель и как его использовать?

Ответ 1

Указатель на void - это "общий" тип указателя. A void * может быть преобразован в любой другой тип указателя без явного приведения. Вы не можете разыгрывать void * или делать с ним арифметику указателя; вы должны сначала преобразовать его в указатель на полный тип данных.

Он используется в тех местах, где вам нужно работать с разными типами указателей в одном и том же коде. Одним из распространенных примеров является библиотечная функция qsort:

void qsort(void *base, size_t nmemb, size_t size, 
           int (*compar)(const void *, const void *));

base - это адрес массива, nmemb - количество элементов в массиве, size - размер каждого элемента, а compar - указатель на функцию, которая сравнивает два элемента массив. Он называется так:

int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);

Выражения массива iArr, dArr и lArr неявно преобразуются из типов массивов в типы указателей в вызове функции, и каждый из них неявно преобразуется из "указателя на int/double/long" to "указатель на void".

Функции сравнения выглядят примерно так:

int compareInt(const void *lhs, const void *rhs)
{
  const int *x = lhs;  // convert void * to int * by assignment
  const int *y = rhs;

  if (*x > *y) return 1;
  if (*x == *y) return 0;
  return -1;
}

Принимая void *, qsort может работать с массивами любого типа.

Недостатком использования void * является то, что вы выбрасываете защиту типа из окна и в встречный трафик. Нет ничего, чтобы защитить вас от неправильной процедуры сравнения:

qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);

compareInt ожидает, что его аргументы будут указывать на int s, но на самом деле работает с double s. Невозможно поймать эту проблему во время компиляции; вы просто закончите с неправильно отсортированным массивом.

Ответ 2

Использование void * означает, что функция может принимать указатель, который не должен быть конкретным типом. Например, в функциях сокетов у вас есть

send(void * pData, int nLength)

это означает, что вы можете вызвать его разными способами, например

char * data = "blah";
send(data, strlen(data));

POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));

Ответ 3

void*

является "указателем на память без предположений, какой тип хранится". Вы можете использовать, например, если вы хотите передать аргумент функции, и этот аргумент может быть нескольких типов и в функции вы будете обрабатывать каждый тип.

Ответ 4

Вы можете посмотреть эту статью о указателях http://www.cplusplus.com/doc/tutorial/pointers/ и прочитать главу: указатели void.

Это также работает для языка C.

Тип указателя типа void является специальным типом указателя. В С++, void представляет отсутствие типа, поэтому указатели void являются указателями, которые укажите значение, которое не имеет типа (и, следовательно, также неопределенного длина и неопределенные свойства разыменования).

Это позволяет указателям void указывать на любой тип данных, начиная с целого числа значение или float для строки символов. Но взамен они большое ограничение: данные, указанные ими, не могут быть напрямую разыменованный (что логично, поскольку у нас нет типа разыменования), и по этой причине нам всегда придется указывать адрес в указатель void на другой тип указателя, указывающий на конкретный тип данных до разыменования его.

Ответ 5

C замечателен в этом отношении. Можно сказать пустота - это небытие void * - это все (может быть каждый)

Это просто крошечный *, который имеет значение.

Рене указал на это. Пустота * является указателем на какое-то место. То, что есть, как "интерпретировать", остается для пользователя.

Это единственный способ иметь непрозрачные типы в C. Очень яркие примеры можно найти, например, в glib или общих библиотеках структуры данных. Он очень подробно разбирался в "C-интерфейсах и реализациях".

Я предлагаю вам прочитать полную главу и попытаться понять концепцию указателя на "получить".

Ответ 6

Указатель void известен как общий указатель. Я хотел бы объяснить примерный сценарий pthread.

Функция потока будет иметь прототип как

void *(*start_routine)(void*)

Дизайнеры API pthread рассматривали аргумент и возвращали значения функции потока. Если эти вещи сделаны родовыми, мы можем набирать cast для void * при отправке в качестве аргумента. аналогично возвращаемое значение может быть получено из void * (Но я никогда не использовал возвращаемые значения из функции потока).

void *PrintHello(void *threadid)
{
   long tid;

   // ***Arg sent in main is retrieved   ***
   tid = (long)threadid;
   printf("Hello World! It me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main (int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0; t<NUM_THREADS; t++){
      //*** t will be type cast to void* and send as argument.
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);   
      if (rc){
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }    
   /* Last thing that main() should do */
   pthread_exit(NULL);
}

Ответ 7

a void* является указателем, но тип того, на что он указывает, не указан. Когда вы передаете указатель void на функцию, вам нужно знать, что это за тип, чтобы вернуть его обратно к этому правильному типу позже в функции, чтобы использовать его. Вы увидите примеры из pthreads, которые используют функции с прототипом в вашем примере, которые используются как функция потока. Затем вы можете использовать аргумент void* в качестве указателя на общий тип данных по вашему выбору, а затем применить его обратно к этому типу для использования в вашей функции потока. Вы должны быть осторожны при использовании указателей void, хотя, если вы не вернетесь к указателю своего истинного типа, вы можете столкнуться со всеми проблемами.

Ответ 8

Стандарт C11 (n1570) §6.2.2.3 al1 p55 говорит:

Указатель на void может быть преобразован в указатель или из указателя на любой объект тип. Указатель на любой тип объекта может быть преобразован в указатель на пустота и обратно; результат сравнивается с оригиналом указатель.

Вы можете использовать этот общий указатель для хранения указателя на любой тип объекта, но вы не можете использовать с ним обычные арифметические операции, и вы не можете его уважать.

Ответ 9

Функция принимает указатель на произвольный тип и возвращает один из них.

Ответ 10

это означает, что указатель вы можете использовать эту ссылку, чтобы получить больше информации о указателе http://www.cprogramming.com/tutorial/c/lesson6.html

Ответ 11

Значение VOID перед именем функции означает, что оно ничего не возвращает. Просто делайте что-нибудь. С другой стороны, VOID как параметр делает его общей функцией, которая может принимать любой тип параметра. Но вы должны предоставить функцию с размером этого параметра.