Связь массивов и указателей

Каждая область памяти, которую вы используете для хранения данных, имеет свой адрес. Адрес позволяет компьютеру обращаться к определенному элементу данных.

Указатель – это переменная, которая хранит адрес другой переменной определенного типа. Фактически это просто адрес одного байта оперативной памяти, с которого начинается размещение этой переменной. Переменная указатель, как и любая другая переменная, обладает именем, а также имеет тип, определяющий то, на какого рода данные она может указывать. При помощи указателей удобно выполнить многие действия:

· получить доступ к содержимому любой ячейки памяти;

· создать новые объекты (переменные, массивы, структуры и т. п.) в процессе выполнения программы путем динамического резервирования памяти;

· облегчить передачу фактических параметров функциям, передавая не сам объект (который может быть довольно большим), а только его начальный адрес.

Как и любая переменная, каждый указатель должен быть описан и инициализирован перед использованием.

Чтобы объявить указатель, нужно выбрать для него имя, перед которым поставить звездочку, а перед звездочкой записать тип переменной или объекта, чей адрес будет храниться в указателе.

Приведем примеры объявления простейших указателей:

int *p1; – указатель на целое;

char *p2; – указатель на символ;

double *p3; – указатель на вещественную переменную

двойной точности.

Особенностью языка С++ является тот факт, что имя одномерного массива (без индексов и квадратных скобок!) можно рассматривать как переменную, всегда хранящую адрес начала массива, т. е. как неизменяемыйуказатель на первый байт участка памяти, выделенного под массив. Ему невозможно присвоить другой адрес, но продублировать этот адрес в другой указатель удобно и выгодно:

int first[] = {3, 7, 4, 0, 25}; /* Объявляем массив целых */

nt *pt; /* Вводим указатель на целое */

pt = first; /* Заносим в указатель адрес начала массива */

Занесение адреса в указатель можно было реализовать и по-другому:

pt = &first[0];

Учтите, что операцию взятия адреса нельзя применять к имени массива, поскольку оно уже адекватно адресу начала массива:

pt = &first; /* Так писать нельзя ! */

Может возникнуть вполне законный вопрос – зачем произведено дублирование адреса? Ведь теперь first и pt адресуют один и тот же байт памяти. Однако имя массива по определению – это неизменяемый указатель, а указатель pt можно изменять, т. е. допустимо изменять адрес, который он содержит. Тем самым мы можем "сдвинуть" указатель так, чтобы он стал указывать на нужный элемент массива. Этот "сдвиг" происходит при выполнении над указателями разрешенных арифметических операций.

Арифметические операции над указателями

Над указателями (т. е. адресами) можно производить некоторые целочисленныеарифметические операции. Чаще всего к указателю прибавляют целую константу или вычитают ее из указателя. Очень важно понять, что эти действия выполняются в единицах того типа, который указан при объявлении указателя. Так, если в программе объявлен указатель на целый объект:

int *pt;

и в него занесен некоторый адрес:

pt = first;

то адрес (pt+1) будет отстоять от исходного на размер целой переменной. Если по этому адресу размещена целая величина, то доступ к ней можно получить способом разадресации указателя: *(pt+1). Заметим, что это можно реализовать и по-другому: *(++pt).

Такая запись предписывает сначала увеличить адрес, хранимый в указателе, на sizeof(type) единиц, а затем извлечь операнд, размещенный по новому адресу.

Даже если не введен изменяемый указатель-дублер, то выражение (first+k) также определяет адрес k-го элемента, а выражение *(first+k) – содержимое по данному адресу. Никакого нарушения синтаксиса здесь нет, поскольку указатель-константа, каковым является имя массива, не изменяется, а лишь участвует в выражении на правах слагаемого. Однако записи first++ или *first++ недопустимы.

При желании указатель, которому присвоен адрес начала массива, можно использовать вместо имени массива при обращении к элементу массива по индексу. Так, вполне корректно писать:

int first[] = {....};

int *pt,m;

pt = first;

m = pt[3];

Последний оператор можно записать и иначе:

а) m = first[3];

б) m = *(first+3);

Приведем текст простой программы, создающей одномерный целочисленный массив и заполняющей его первыми числами натурального ряда чисел:

int mas[10], i;

for(i = 0; i < 10; i++)

mas[i] = i;

При обращении к элементам массива через указатель один из возможных вариантов таков:

int mas[10], i, *pt;

pt = mas;

for(i = 0; i < 10; i++)

*pt++ = i;

Конечно, можно обойтись и без указателя pt:

int mas[10], i;

for(i = 0; i < 10; i++)

*(mas+i) = i;