Выбрать главу

Дополнительные операции с указателями будут описаны в разделе 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.3.2

Упражнение 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