Почему (void **) type может быть присвоен (void *) или (int *)?
это мой код C:
int main()
{
void * ptr_void;
void ** ptr_2void;
ptr_void = ptr_2void;
return 0;
}
Мне просто интересно, почему этот код действителен? Я назначил (void *)
to (void **)
, компилятор передает его даже без предупреждения. тип выглядит несоответствующим. и также работает следующий код, который назначает (void **)
- (int *)
.
int main()
{
int * ptr_int;
void ** ptr_2void;
ptr_int = ptr_2void;
return 0;
}
кто-нибудь может понять, что именно находится в (void *)
?
Ответы
Ответ 1
Указатели void типа неявно преобразуются в указатели на любой другой тип данных. Компилятор не будет показывать никаких предупреждений. Аналогично преобразование типа из указателя любого типа в void *
также будет работать без предупреждения.
Кроме указателей void, если вы попытаетесь преобразовать из одного типа указателя в другой тип указателя, неявно будет выдано предупреждение компилятором.
Например, рассмотрим приведенный ниже код. Он даст вам предупреждение "assignment from incompatible pointer type
".
int *intptr;
void *voidptr;
void **vvptr;
int intval=123;
voidptr=&intval;
vvptr=voidptr;
intptr=vvptr;
Строка кода, вызывающая предупреждение, intptr=vvptr;
, потому что intptr
является integer pointer
и vvptr
является указателем типа void **
. Ни один из них не является указателями void *
и, следовательно, предупреждением.
Чтобы избежать этого предупреждения, вы должны явно ввести тип void **
в тип int *
. Если вы измените строку intptr=vvptr;
на intptr=(int *)vvptr;
, предупреждение не будет показано компилятором.
Ответ 2
Важно различать преобразование и литье.
Преобразование преобразует значение одного типа в значение другого типа. Листинг - это оператор (состоящий из имени типа в круглых скобках), который явно указывает преобразование. Преобразование может быть либо явным (задано оператором литья), либо неявным. Для большинства преобразований указателей требуется оператор трансляции; исключения указателей, включающие void*
, являются исключением.
Значение любого типа указатель-объект (или тип от указателя к неполному) может быть преобразовано в void*
и обратно в исходный тип; результирующий указатель гарантированно сравним с исходным указателем.
В присваивании (или при передаче аргумента функции или в инструкции return
) преобразование в или из void*
может выполняться неявно, без оператора трансляции.
В первом примере кода:
void * ptr_void;
void ** ptr_2void;
ptr_void = ptr_2void;
присвоение разрешено, поскольку void**
может быть преобразовано в void*
без трансляции. Здесь нет ничего особенного в void**
; указатель на что-либо может быть преобразован в void*
без трансляции. (void*
- общий тип указателя; void**
не является общим типом указателя на указатель, и на самом деле не существует общего типа указателя на указатель.)
В вашем втором примере кода:
int * ptr_int;
void ** ptr_2void;
ptr_int = ptr_2void;
присвоение недействительно; это нарушение ограничения. Нет никакого неявного преобразования между int*
и void**
, так как ни один из них не является void*
. Любой соответствующий компилятор C должен выдать диагностическое сообщение для назначения. В некоторых случаях диагностика может быть предупреждением, и компилятор, вероятно, генерирует неявное преобразование, как если бы вы написали акт. В других случаях компилятор может потребовать дополнительных параметров, чтобы заставить его диагностировать это нарушение.
Обратите внимание, что указанное выше не относится к указателям на функции. Любой тип указателя функции может быть преобразован (с актом) в любой другой тип указателя функции, преобразование указателя функции в void*
или наоборот имеет поведение undefined (хотя оно может поддерживаться некоторыми компиляторами).
Ответ 3
void**
и void*
- разные типы. int*
и void**
тоже разные типы. Но, как говорит Бармар, any data pointer type can be cast to/from void*.
Это означает, что вы можете использовать int*
для void*
, но вы не можете использовать int*
для void**
, так как void**
не имеет этого же специального свойства.
gcc
должно выдать предупреждение:
warning: assignment from incompatible pointer type [enabled by default]
ptr_int = ptr_2void;
Смотрите этот вопрос: Переход к void ** вместо void * заставляет компилятор жаловаться на типы, почему?
void * - это тип, который неявно конвертируется в любой объект и из него тип указателя. void ** - нет, поэтому, если вы можете назначить char * void *, вы не можете сделать то же самое с char ** и void **.
Причина в том, что они являются несовместимыми типами: char ** указывает на char *, void ** указывает на пустоту *, поэтому их базовые типы не совпадают.