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
Этот стиль подчеркивает, что переменная имеет составной тип. Согласно второму, модификатор типа располагается рядом с типом, но он определяет только одну переменную в операторе:
int* p1; // p1 - указатель на тип int
int* p2; // p2 - указатель на тип int
Этот стиль подчеркивает, что объявление определяет составной тип.
Нет никакого единственно правильного способа определения указателей и ссылок. Важно неукоснительно придерживаться выбранного стиля.
В этой книге используется первый стиль, знак * (или &) помещается рядом с именем переменной.
Указатели на указатели
Теоретически нет предела количеству модификаторов типа, применяемых в операторе объявления. Когда модификаторов более одного, они объединяются хоть и логичным, но не всегда очевидным способом. В качестве примера рассмотрим указатель. Указатель — это объект в памяти, и, как у любого объекта, у этого есть адрес. Поэтому можно сохранить адрес указателя в другом указателе.
Каждый уровень указателя отмечается собственным символом *. Таким образом, для указателя на указатель пишут **, для указателя на указатель на указатель — *** и т.д.
int ival = 1024;
int *pi = &ival; // pi указывает на переменную типа int
int **ppi = π // ppi указывает на указатель на переменную типа int
Здесь pi — указатель на переменную типа int, a ppi — указатель на указатель на переменную типа. Эти объекты можно было бы представить так:
Подобно тому, как обращение к значению указателя на переменную типа int возвращает значение типа int, обращение к значению указателя на указатель возвращает указатель. Для доступа к основной объекту в этом случае необходимо обратиться к значению указателя дважды:
cout << "The value of ival "
<< "direct value: " << ival << " "
<< "indirect value: " << *pi << " "
<< "doubly indirect value: " << **ppi << endl;
Эта программа выводит значение переменной ival тремя разными способами: сначала непосредственно, затем через указатель pi на тип int и наконец обращением к значению указателя ppi дважды, чтобы добраться до основного значения в переменной ival.
Ссылки на указатели
Ссылка — не объект. Следовательно, не может быть указателя на ссылку. Но поскольку указатель — это объект, вполне можно определить ссылку на указатель.
int i = 42;
int *p; // p - указатель на тип int
int *&r = p; // r - ссылка на указатель p
r = &i; // r ссылается на указатель;
// присвоение &i ссылке r делает p указателем на i
*r = 0; // обращение к значению r дает i, объект, на который
// указывает p; изменяет значение i на 0
Проще всего понять тип r — прочитать определение справа налево. Ближайший символ к имени переменной (в данном случае & в &r) непосредственно влияет на тип переменной. Таким образом, становится ясно, что r является ссылкой. Остальная часть оператора объявления определяет тип, на который ссылается ссылка r. Следующий символ, в данном случае *, указывает, что тип r относится к типу указателя. И наконец, базовый тип объявления указывает, что r — это ссылка на указатель на переменную типа int.
Сложное объявление указателя или ссылки может быть проще понять, если читать его справа налево.
Упражнения раздела 2.3.3
Упражнение 2.25. Определите типы и значения каждой из следующих переменных:
(a) int* ip, &r = ip; (b) int i, *ip = 0; (c) int* ip, ip2;
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК