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

147

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

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

OneZero
источник
2
Какую книгу C вы используете? Вы просите лучшую часть целой главы.
cnicutar
1
Взгляните на stackoverflow.com/questions/692564/…
WDan
Ответ
Возьмите реплику с mallocи calloc. Страница man продолжает: «... возвращает указатель на выделенную память, которая соответствующим образом выровнена для любого встроенного типа данных».
автомат

Ответы:

175

Указатель на voidэто «универсальный» тип указателя. A void *может быть преобразован в любой другой тип указателя без явного приведения. Вы не можете разыменовывать 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» в «указатель 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ожидает, что его аргументы будут указывать на ints, но на самом деле работает с doubles. Нет способа поймать эту проблему во время компиляции; вы просто получите пропущенный массив.

Джон Боде
источник
5
На самом деле не гарантируется, что a void*может быть приведен к указателю на функцию. Но для указателей данных то, что вы сказали, имеет место.
Ватин
До того, как указатели на void стали доступны, вместо них использовался «char *». Но пустота лучше, поскольку она не может быть использована для непосредственного изменения чего-либо.
user50619
22

Использование 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));
TheSteve
источник
Так что это почти как дженерики в других языках, но без проверки типов, верно?
Вингер Сендон
3
Я полагаю, что это будет похоже, однако, поскольку нет проверки типов, ошибка может привести к очень странным результатам или вызвать полный сбой программы.
TheSteve
7

С примечателен в этом отношении. Можно сказать, voidчто ничто void*есть все (может быть все).

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

Рене указал на это. void *Является указателем на какое - то место. То, что есть как «интерпретировать», остается за пользователем.

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

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

Фридрих
источник
5
void*

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

Рене Коларжик
источник
3

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

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

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

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

AG
источник
3

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

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

void *(*start_routine)(void*)

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

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

   // ***Arg sent in main is retrieved   ***
   tid = (long)threadid;
   printf("Hello World! It's 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);
}
Jeyaram
источник
Почему звоните, pthread_exit(NULL);а return 0;не в конце основного?
Seabass77
1
@ Seabass77 Пожалуйста, обратитесь к stackoverflow.com/questions/3559463/…
Jeyaram
1

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

mathematician1975
источник
1

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

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

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

md5
источник
0

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

glglgl
источник