Сегодня, когда я читал код других, я видел что-то вроде void *func(void* i);
, что означает это void*
здесь для имени функции и для типа переменной, соответственно?
Кроме того, когда нам нужно использовать этот указатель и как его использовать?
Сегодня, когда я читал код других, я видел что-то вроде void *func(void* i);
, что означает это void*
здесь для имени функции и для типа переменной, соответственно?
Кроме того, когда нам нужно использовать этот указатель и как его использовать?
Указатель на 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. Невозможно поймать эту проблему во время компиляции; вы просто закончите с неправильно отсортированным массивом.
Использование 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));
void*
является "указателем на память без предположений, какой тип хранится". Вы можете использовать, например, если вы хотите передать аргумент функции, и этот аргумент может быть нескольких типов и в функции вы будете обрабатывать каждый тип.
Вы можете посмотреть эту статью о указателях http://www.cplusplus.com/doc/tutorial/pointers/ и прочитать главу: указатели void.
Это также работает для языка C.
Тип указателя типа void является специальным типом указателя. В С++, void представляет отсутствие типа, поэтому указатели void являются указателями, которые укажите значение, которое не имеет типа (и, следовательно, также неопределенного длина и неопределенные свойства разыменования).
Это позволяет указателям void указывать на любой тип данных, начиная с целого числа значение или float для строки символов. Но взамен они большое ограничение: данные, указанные ими, не могут быть напрямую разыменованный (что логично, поскольку у нас нет типа разыменования), и по этой причине нам всегда придется указывать адрес в указатель void на другой тип указателя, указывающий на конкретный тип данных до разыменования его.
C замечателен в этом отношении. Можно сказать пустота - это небытие void * - это все (может быть каждый)
Это просто крошечный *, который имеет значение.
Рене указал на это. Пустота * является указателем на какое-то место. То, что есть, как "интерпретировать", остается для пользователя.Это единственный способ иметь непрозрачные типы в C. Очень яркие примеры можно найти, например, в glib или общих библиотеках структуры данных. Он очень подробно разбирался в "C-интерфейсах и реализациях".
Я предлагаю вам прочитать полную главу и попытаться понять концепцию указателя на "получить".
Указатель 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);
}
a void*
является указателем, но тип того, на что он указывает, не указан. Когда вы передаете указатель void на функцию, вам нужно знать, что это за тип, чтобы вернуть его обратно к этому правильному типу позже в функции, чтобы использовать его. Вы увидите примеры из pthreads
, которые используют функции с прототипом в вашем примере, которые используются как функция потока. Затем вы можете использовать аргумент void*
в качестве указателя на общий тип данных по вашему выбору, а затем применить его обратно к этому типу для использования в вашей функции потока. Вы должны быть осторожны при использовании указателей void, хотя, если вы не вернетесь к указателю своего истинного типа, вы можете столкнуться со всеми проблемами.
Стандарт C11 (n1570) §6.2.2.3 al1 p55 говорит:
Указатель на
void
может быть преобразован в указатель или из указателя на любой объект тип. Указатель на любой тип объекта может быть преобразован в указатель на пустота и обратно; результат сравнивается с оригиналом указатель.
Вы можете использовать этот общий указатель для хранения указателя на любой тип объекта, но вы не можете использовать с ним обычные арифметические операции, и вы не можете его уважать.
Функция принимает указатель на произвольный тип и возвращает один из них.
это означает, что указатель вы можете использовать эту ссылку, чтобы получить больше информации о указателе http://www.cprogramming.com/tutorial/c/lesson6.html
Значение VOID перед именем функции означает, что оно ничего не возвращает. Просто делайте что-нибудь. С другой стороны, VOID как параметр делает его общей функцией, которая может принимать любой тип параметра. Но вы должны предоставить функцию с размером этого параметра.