
Int main()
{
using namespace std;
double * p3 = new double [3];
// space for 3 doubles
p3[0] = 0.2;
// treat p3 like an array name
p3[1] = 0.5;
p3[2] = 0.8;
cout << "p3[1] is " << p3[1] << ".\n";
p3 = p3 + 1;
// increment the pointer
cout << "Now p3[0] is " << p3[0] << " and ";
cout << "p3[1] is " << p3[1] << ".\n";
p3 = p3 - 1;
// point back to beginning
delete [] p3;
// free the memory
cin.get();
return 0;
}
Как видите, программа использует указатель рЗ, как если бы он был именем массива: р3 [ 0 ] для первого элемента и т.д. Фундаментальное отличие между именем массива и указателем проявляется в следующей строке:
рЗ = рЗ + 1; // допускается для указателей, но не для имен массивов
Вы не можете изменить значение для имени массива. Но указатель — переменная, а потому ее значение можно изменить. Обратите внимание на эффект от добавления 1 к рЗ. Теперь выражение рЗ [0] ссылается на бывший второй элемент массива. То есть добавление 1 к рЗ заставляет рЗ указывать на второй элемент вместо первого. Вычитание 1 из значения указателя возвращает его назад, в исходное значение, поэтому программа может применить delete [ ] с корректным адресом.
Действительные адреса соседних элементов int отличаются на 2 или 4 байта, поэтому тот факт, что добавление 1 к рЗ дает адрес следующего элемента, говорит о том, что арифметика указателей устроена специальным образом. Так оно и есть на самом деле.
Подведение итогов относительно указателей
Позже вы сможете несколько углубить свои знания об указателях, а пока подведем итоги относительно того, что известно об указателях и массивах к настоящему моменту.
Объявление указателей
Чтобы объявить указатель на определенный тип, нужно использовать следующую форму:
имяТипа * имяУказателя;
Вот некоторые примеры:
double * рn; // рn может указывать на значение double
char * рс; // рс может указывать на значение char
Здесь рn и рс — указатели, a double * и char * — нотация C++ для представления указателя на double и указателя на char.
Присваивание значений указателям
Указателям должны быть присвоены адреса памяти. Можно применить операцию & к имени переменной, чтобы получить адрес именованной области памяти, либо операцию new, которая возвращает адрес неименованной памяти.
Вот некоторые примеры:
double * рn; // рn может указывать на значение double
double * pa; // так же и ра
char * рс; // рс может указывать на значение char
double bubble = 3.2;
рn = &bubble; // присваивание адреса bubble переменной рn
рс = new char; // присваивание адреса выделенной памяти char
// переменной рс
pa = new double [30] ; // присваивание адреса массива из 30 double переменной ра
Разыменование указателей
Разыменование указателя — это получение значения, на которое он указывает. Для этого к указателю применяется операция разыменования (*). То есть, если рn — указатель на bubble, как в предыдущем примере, то *рn — значение, на которое он указывает, в данном случае — 3.2.
Вот некоторые примеры:
cout << *рn; // вывод значения bubble
*рс = 'S'; // помещение 'S' в область памяти, на которую указывает рс
Нотация массивов — второй способ разыменования указателя; например, рn [ 0 ] — это то же самое, что и *рn. Никогда не следует разыменовывать указатель, который не был инициализирован правильным адресом.
Различие между указателем и указываемым значением
Помните, если pt — указатель на int, то *pt — не указатель на int, а полный эквивалент переменной типа int. Указателем является просто pt.
Вот некоторые примеры:
int * pt = new int; // присваивание адреса переменной pt
*pt = 5; //сохранение 5 по этому адресу
Имена массивов
В большинстве контекстов C++ трактует имя массива как эквивалент адреса его первого элемента.
Вот пример:
int tacos[10]; // теперь tacos - то же самое, что и &tacos[0]
Одно исключение из этого правила связано с применением операции sizeof к имени массива. В этом случае sizeof возвращает размер всего массива в байтах.
Арифметика указателей
C++ позволяет добавлять целые числа к указателю. Результат добавления к указателю единицы равен исходному адресу плюс значение, эквивалентное количеству байт в указываемом объекте. Можно также вычесть один указатель из другого, чтобы получить разницу между двумя указателями. Последняя операция, которая возвращает целочисленное значение, имеет смысл только в случае, когда два указателя указывают на элементы одного и того же массива (указание одной из позиций за границей массива также допускается); при этом результат означает расстояние между элементами массива.
Динамическое и статическое связывание для массивов
Объявление массива можно использовать для создания массива со статическим связыванием — т.е. массива, размер которого фиксирован на этапе компиляции: