Дополнительные операции с указателями будут описаны в разделе 3.5.3.
Тип void*
является специальным типом указателя, способного содержать адрес любого объекта. Подобно любому другому указателю, указатель void*
содержит адрес, но тип объекта по этому адресу неизвестен.
double obj = 3.14, *pd = &obj;
// ok: void* может содержать адрес любого типа данных
void *pv = &obj; // obj может быть объектом любого типа
pv = pd; // pv может содержать указатель на любой тип
С указателем void*
допустимо немного действий: его можно сравнить с другим указателем, можно передать его функции или возвратить из нее либо присвоить другому указателю типа void*
. Его нельзя использовать для работы с объектом, адрес которого он содержит, поскольку неизвестен тип объекта, неизвестны и операции, которые можно с ним выполнять.
Как правило, указатель void*
используют для работы с памятью как с областью памяти, а не для доступа к объекту, хранящемуся в этой области. Использование указателей void*
рассматривается в разделе 19.1.1, а в разделе 4.11.3 продемонстрировано, как можно получить адрес, хранящийся в указателе void*
.
Упражнение 2.18. Напишите код, изменяющий значение указателя. Напишите код для изменения значения, на которое указывает указатель.
Упражнение 2.19. Объясните основные отличия между указателями и ссылками.
Упражнение 2.20. Что делает следующая программа?
int i = 42;
int *p1 = &i;
*p1 = *p1 * *p1;
Упражнение 2.21. Объясните каждое из следующих определений. Укажите, все ли они корректны и почему.
int i = 0;
(a) double* dp = &i; (b) int *ip = i; (c) int *p = &i;
Упражнение 2.22. С учетом того, что p
является указателем на тип int
, объясните следующий код:
if (p) // ...
if (*p) // ...
Упражнение 2.23. Есть указатель p
, можно ли определить, указывает ли он на допустимый объект? Если да, то как? Если нет, то почему?
Упражнение 2.24. Почему инициализация указателя p
допустима, а указателя lp
нет?
int i = 42; void *p = &i; long *lp = &i;
2.3.3. Понятие описаний составных типов
Как уже упоминалось, определение переменной состоит из указания базового типа и списка операторов объявления. Каждый оператор объявления может связать свою переменную с базовым типом отлично от других операторов объявления в том же определении. Таким образом, одно определение может определять переменные отличных типов.
// i - переменная типа int; p - указатель на тип int;
// r - ссылка на тип int
int i = 1024, *p = &i, &r = i;
Многие программисты не понимают взаимодействия базового и модифицированного типа, который может быть частью оператора объявления.
Весьма распространенное заблуждение полагать, что модификатор типа (*
или &
) применяется ко всем переменным, определенным в одном операторе. Частично причина в том, что между модификатором типа и объявляемым именем может находиться пробел.
int* p; // вполне допустимо, но может ввести в заблуждение
Данное определение может ввести в заблуждение потому, что создается впечатление, будто int*
является типом каждой переменной, объявленной в этом операторе. Несмотря на внешний вид, базовым типом этого объявления является int
, а не int*
. Символ *
— это модификатор типа p
, он не имеет никакого отношения к любым другим объектам, которые могли бы быть объявлены в том же операторе:
int* p1, p2; // p1 - указатель на тип int; p2 - переменная типа int
Есть два общепринятых стиля определения нескольких переменных с типом указателя или ссылки. Согласно первому, модификатор типа располагается рядом с идентификатором:
int *p1, *p2; // p1 и p2 — указатели на тип int