В английском языке void как существительное определяется как«пустота, пространство, промежуток», а в языке C void переводится как«отсутствие типа», соответственноvoid * — это«указатель без типа»。
void, по-видимому, служит только в качестве «комментария» и для ограничения программы. Конечно, «комментарий» здесь не для нас, людей, а так называемый комментарий, предоставляемый компилятору.
Роль void
1. Ограничение возвращаемых значений функции; это распространённый случай.
2. Ограничение параметров функции; это также довольно часто встречается.
Обычно это два случая, которые мы часто встречаем:
- Когда функция не должна возвращать значение, необходимо использовать void, чтобы ограничить её. Это первый случай, который мы упомянули. Например: void func(int a, char *b).
- Когда функция не должна принимать параметры, необходимо использовать void, чтобы ограничить её. Это второй случай, который мы упомянули. Например: int func(void).
Правила использования указателей void
1. Указатель void может указывать на данные любого типа, то есть любой тип указателя можно присвоить указателю void. Например:
int *a; void *p; p=a;
Если вы хотите присвоить указатель void p указателю другого типа, вам нужно приведение типа. В этом случае:a=(int *)pПри выделении памяти мы можем увидеть использование указателей void: указатель, возвращаемый функцией выделения памяти malloc, имеетvoid *тип. При использовании этого указателя пользователь должен выполнить приведение типа, то есть явно указать, данные какого типа хранятся в памяти, на которую указывает указатель.(int *)malloc(1024)Это означает явное указание, что память, на которую указывает возвращённый malloc указатель void*, хранит данные int по одному.
2. В стандарте ANSI C арифметические операции над указателями void, такие как p++ или p+=1, не допускаются, поскольку void не имеет типа, и мы не знаем, над сколькими байтами должна выполняться каждая арифметическая операция. Например, тип char оперирует sizeof(char) байтами, тогда как int должен оперировать sizeof(int) байтами. Однако в GNU это разрешено, потому что по умолчанию GNU считает, чтоvoid *иchar *— это одно и то же. Поскольку это определённо, арифметические операции, безусловно, можно выполнять. Здесьsizeof(*p)==sizeof(char)。
void почти всегда служит только "комментарием" и для ограничения программы, потому что никто никогда не определяет переменную void. Давайте попробуем определить её:
void a;
Эта строка вызовет ошибку компиляции с сообщением"недопустимое использование типа 'void'"Даже еслиvoid aпри компиляции не вызывает ошибки, это не имеет практического смысла.
Как известно, если указатели p1 и p2 имеют один и тот же тип, то мы можем напрямую присваивать между p1 и p2; если p1 и p2 указывают на данные разных типов, мы должны использовать оператор приведения типа, чтобы преобразовать тип указателя справа от оператора присваивания к типу указателя слева.
float *p1; int *p2; p1 = p2; //其中p1 = p2语句会编译出错, //提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为: p1 = (float *)p2;
иvoid *Но void* отличается; указатель любого типа можно напрямую присвоить ему без приведения типа.
void *p1; int *p2; p1 = p2;
Но это не означает, чтоvoid *его также можно присваивать указателям других типов без приведения типа. Потому что «отсутствие типа» может вмещать «типизированное», но «типизированное» не может вмещать «отсутствие типа».
Будьте осторожны с типами void-указателей:
Согласно стандарту ANSI (Американский национальный институт стандартов), арифметические операции над void-указателями не допускаются; то есть все следующие операции недопустимы:
void * pvoid; pvoid++; //ANSI:错误 pvoid += 1; //ANSI:错误 //ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。 //例如: int *pint; pint++; //ANSI:正确Результат pint++ — это увеличение его на sizeof(int).
Но GNU считает иначе; он определяет, что арифметические операции дляvoid *совпадают с операциями дляchar *. Поэтому следующие операторы корректны в компиляторах GNU:
pvoid++; //GNU:正确 pvoid += 1; //GNU:正确
Результат выполнения pvoid++ состоит в том, что он увеличивается на 1.
В реальном программировании, чтобы соответствовать стандарту ANSI и повысить переносимость программы, мы можем написать код, реализующий ту же функциональность следующим образом:
void * pvoid; ((char *)pvoid)++; //ANSI:错误;GNU:正确 (char *)pvoid += 1; //ANSI:错误;GNU:正确
У GNU и ANSI также есть некоторые различия. В целом GNU более «открыт», чем ANSI, и обеспечивает поддержку большего количества синтаксиса. Но в реальном проектировании нам всё же следует стараться по возможности соответствовать стандарту ANSI. Если параметр функции может быть указателем любого типа, то его параметр следует объявлять какvoid *。
Примечание:void-указатель может указывать на данные любого типа, что может принести нам некоторую пользу в программах. Когда параметр функции имеет тип указателя, мы можем определить его как void-указатель, чтобы функция могла принимать указатели любого типа. Например:
Типичными примерами являются прототипы функций операций с памятью memcpy и memset соответственно:
void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num );
Таким образом, указатели любого типа можно передавать в memcpy и memset. Это действительно отражает смысл функций операций с памятью, потому что объект, с которым они работают, — это всего лишь фрагмент памяти, независимо от того, какого типа эта память (см. «Реализация обобщённого программирования на языке C»). Если бы типы параметров memcpy и memset были неvoid *, аchar *, это было бы поистине странно! Такие memcpy и memset, очевидно, не являются «чистыми, изящными» функциями! Появление void вызвано лишь потребностью в абстракции. Если вы правильно понимаете понятие «абстрактного базового класса» в объектно-ориентированном программировании, то также легко поймёте тип данных void. Точно так же, как нельзя определить экземпляр абстрактного базового класса, мы также не можем определить переменную void (назовём void по аналогии «абстрактным типом данных»).
Исходный URL: https://www.cnblogs.com/wuyudong/p/c-void-point.html