Типы данных в языке C++__________________________________685

С указателями может выполняться ограниченное количество арифметических
операций. Указатель можно увеличивать (++), уменьшать (——), складывать с ука-
зателем целые числа (+ или +=), вычитать из него целые числа (- или -=) или вы-
читать один указатель из другого.

Сложение указателей с целыми числами отличается от обычной арифметики.
Прибавить к указателю 1 означает сдвинуть его на число байтов, содержащихся в
переменной, на которую он указывал. Обычно подобные операции применяются к
указателям на массивы. Если продолжить приведенный выше пример, в котором
указателю Pt было присвоено значение b — указателя на первый элемент массива,
то после выполнения оператора

Pt += 2;

Pt будет указывать на третий элемент массива b. Истинное же значение указа-
теля Pt изменится на число байтов, занимаемых одним элементом массива, умно-
женное на 2. Например, если каждый элемент массива b занимает 2 байта, то зна-
чение Pt (т.е. адрес в памяти, на который указывает Pt) увеличится на 4.

Аналогичные правила действуют и при вычитании из указателя целого значения.

Переменные указатели можно вычитать один из другого. Например, если Pt
указывает на первый элемент массива b, а указатель Pti — на третий, то результат
выражения Pti — Pt будет равен 2 — разности индексов элементов, на которые
указывают эти указатели. И так будет, несмотря на то, что адреса, содержащиеся в
этих указателях, различаются на 4 (если элемент массива занимает 2 байта).

Арифметика указателей теряет всякий смысл, если она выполняется не над
указателями на массив. Нельзя полагать, чтоб две переменные одинакового типа
хранятся в памяти вплотную друг к другу, если только они не соседствуют в масси-
ве. Сравнение указателей операциями >, <, >=, <= также имеют смысл только для
указателей на один и тот же массив. Однако, операции отношения == и != имеют
смысл для любых указателей. При этом указатели равны, если они указывают на
один и тот же адрес в памяти.

Указатель можно присваивать другому указателю, если оба указателя имеют
одинаковый тип. В противном случае нужно использовать операцию приведения
типа, чтобы преобразовать значение указателя в правой части присваивания к
типу указателя в левой части присваивания. Исключением из этого правила явля-
ется указатель на void (т.е. void*), который является общим указателем, способ-
ным представлять указатели любого типа. Указателю на void можно присваивать
все типы указателей без приведения типа. Однако указатель на void не может быть
присвоен непосредственно указателю другого типа — указатель на void сначала
должен быть приведен к типу соответствующего указателя.

Мы рассмотрели ранее указатели на массивы. Однако, соотношение между
массивами и указателями может быть и обратным — могут использоваться масси-
вы указателей. Подобные структуры часто используются в массивах строк или в
массивах указателей на различные объекты.

Например, вы можете сделать следующее объявление:

char *Sa[2] = {"Это первая строка", "Вторая"};

Вы объявили массив размером 2 элементов типа (char *). Каждый элемент та-
кого массива — строка. Но в C++ строка является, по существу, указателем на ее
первый символ. Таким образом, каждый элемент в массиве строк в действительно-
сти является указателем на первый символ строки. Каждая строка хранится в па-
мяти как строка, завершающаяся нулевым символом. Число символов в каждой
из строк может быть различным. Таким образом, массив указателей на строки по-
зволяет обеспечить доступ к строкам символов любой длины.

Указатели широко используются при передаче параметров в функции. Особен-
ности использования указателей в этих целях см. в разделе 11.5.2 главы 11.

В разделе 1.6.6 главы 6 подробно рассмотрены указатели на объекты и работа
с ними.