void* pn = static_cast<void*>(&n);
является неявным преобразованием; вы также можете написать
void *pn = &n;
Это означает, что pn
хранит указатель на некоторый тип объекта; программист несет ответственность за знание того, что это за тип объекта. Чтобы отбросить, вам нужен static_cast
:
int *pi = static_cast<int*>(pn);
Обратите внимание, что использование static_cast
для приведения к любому типу, значительно отличающемуся от исходного (float
существенно отличается, const int
нет), является способом повторной интерпретации. Вы должны написать это reinterpret_cast
неявное преобразование (или static_cast
), за которым следует static_cast
.
В этом вся цель void*
, концепции, восходящей к C, где приведения записываются с приведением в стиле C, очевидно (не static_cast
...), но в остальном имеют идентичные значения.
Возвращаясь к синтаксису объявлений в C и C++:
Объявление указателя на int
равно int (*pi);
(скобки бесполезны, но помогают проиллюстрировать суть), вы можете прочитать это так: Я объявляю, что выражение (*pi)
имеет тип int
. Вы можете прочитать объявление функции таким образом: в int f(int i);
я заявляю, что если i
имеет тип int
, то f(i)
имеет тип int
.
Объявление void (*pi);
выглядит как указатель на void
, но не существует такой вещи, как объект типа void
, выражение *pi
даже неправильно сформировано, оно не имеет смысла. Это особый случай в системе типов: синтаксис говорит "указатель на пустоту", семантика говорит "указатель на что-то".
В C и C++ объект-указатель является объектом первого класса, и вы можете взять его адрес и получить указатель на указатель и т. д. (В отличие от Java, где ссылки, подобные другим фундаментальным типам, не являются объектами класса.)
Таким образом, вы можете иметь указатели int**
, int***
... на (указатели... на int
); по той же причине вы можете объявить void**
: как указатель на (указатель на пустоту), семантически указатель на (указатель на что-то).
Точно так же указатель на int
не может быть назначен указателю на float
без приведения:
float *f = &i; // ill-formed
из-за несоответствия типов тип, отличный от void**
, не может быть присвоен void**
: результатом разыменования void**
должен быть объект void*
.
person
curiousguy
schedule
21.09.2018
&foo
как указатель наint*
, аbar
— как указатель наvoid*
. Эти два типа очень разные. Это дополнительный уровень косвенности, который имеет все значение. - person Some programmer dude   schedule 21.09.2018sizeof (int*)
иsizeof (void*)
совпадают. - person Ben Voigt   schedule 21.09.2018void*
? Если быvoid*
был другого размера, разве это не аннулировало бы это требование? - person Jonathan Mee   schedule 21.09.2018void*
достаточно велико, чтобы однозначно представлять любое значение указателя, он может работать. Нет причин, по которымT*
не может быть меньшеvoid*
. - person eerorika   schedule 21.09.2018void*
иchar*
) больше, чем указатели с требованиями к выравниванию (включаяint*
). - person Ben Voigt   schedule 21.09.2018fwrite
в такой системе; скажем, я пытался вывести массив, как я мог знать, что уvoid*
такой же шаг? - person Jonathan Mee   schedule 21.09.2018static_cast<void*>
- это даже не явное преобразование, это неявное преобразование, выполненное явно - person curiousguy   schedule 21.09.2018fwrite
волнует шагvoid*
? (Как говорится в ответе 303, на самом деле его нет, математика указателей сvoid*
незаконна) Вы пытаетесь написать массив пустых указателейvoid* a[]
? Вы можете сделать это, но полученный файл не будет переносимым. Значения указателя имеют значение только в одном и том же адресном пространстве, даже чтение файла в той же самой системе после перезапуска будет бесполезным. - person Ben Voigt   schedule 21.09.2018