char* pc = &ch // указатель на char
int ii = 17;
int* pi = ⅈ // указатель на int
Если мы хотим увидеть значение объекта, на который ссылаемся, то можем применить к указателю оператор разыменования, унарный *
. Рассмотрим пример.
cout << "pc==" << pc << "; содержимое pc==" << *pc << "\n";
cout << "pi==" << pi << "; содержимое pi==" << *pi << "\n";
Значением *pc
является символ c
, а значением *pi
— целое число 17
. Значения переменных pc
и pi
зависят от того, как компилятор размещает переменные ch
и ii
в памяти. Обозначение, используемое для значения указателя (адрес), также может изменяться в зависимости от того, какие соглашения приняты в системе; для обозначения значений указателей часто используются шестнадцатеричные числа (раздел A.2.1.1).
Оператор
*pc = 'x'; // OK: переменной char, на которую ссылается
// указатель pc,
// можно присвоить символ 'x'
*pi = 27; // OK: указатель int* ссылается на int, поэтому *pi —
// это int
*pi = *pc; // OK: символ (*pc) можно присвоить переменной
// типа int (*pi)
int
?” — некорректный вопрос. Ссылаются не целые числа, а указатели. Тип указателя позволяет выполнять операции над адресами, в то время как тип int
позволяет выполнять (арифметические и логические) операции над целыми числами. Итак, указатели и целые числа нельзя смешивать.
int i = pi; // ошибка: нельзя присвоить объект типа int*
// объекту типа int
pi = 7; // ошибка: нельзя присвоить объект типа int объекту
// типа int*
Аналогично, указатель на char
(т.е. char*
) — это не указатель на int
(т.е. int*
). Рассмотрим пример.
pc = pi; // ошибка: нельзя присвоить объект типа int*
// объекту типа char*
pi = pc; // ошибка: нельзя присвоить объект типа char*
// объекту типа int*
Почему нельзя присвоить переменную pc
переменной pi
? Один из ответов — символ char
намного меньше типа int
.
char ch1 = 'a';
char ch2 = 'b';
char ch3 = 'c';
char ch4 = 'd';
int* pi = &ch3 // ссылается на переменную,
// имеющую размер типа char
// ошибка: нельзя присвоить объект char* объекту
// типа int*
// однако представим себе, что это можно сделать
*pi = 12345; // попытка записи в участок памяти, имеющий размер
// типа char
*pi = 67890;
Как именно компилятор размещает переменные в памяти, зависит от его реализации, но, скорее всего, это выглядит следующим образом.
Если бы компилятор пропустил такой код, то мы могли бы записать число 12345
в ячейку памяти, начинающуюся с адреса &ch3
. Это изменило бы содержание окрестной памяти, т.е. значения переменных ch2
и ch4
. В худшем (и самом реальном) случае мы бы перезаписали часть самой переменной pi
! В этом случае следующее присваивание *pi=67890
привело бы к размещению числа 67890
в совершенно другой области памяти. Очень хорошо, что такое присваивание запрещено, но таких механизмов защиты на низком уровне программирования очень мало.
В редких ситуациях, когда нам требуется преобразовать переменную типа int
в указатель или конвертировать один тип показателя в другой, можно использовать оператор reinterpret_cast
(подробнее об этом — в разделе 17.8).