Из арифметических операций к указателям могут применяться только сложение и вычитание, которые имеют ряд особенностей и ограничений.
Пусть объявлен массив и указатель, в котором хранится адрес начала массива: const n=10; float a[n]; float * p=a, *q; unsigned short k=3;
В результате операции сложения адреса с целым числом ( p+k) для целого положительного к получается адрес ячейки, которая находится на k*sizeof(тип) байт “правее” ячейки с адресом p, где тип — тип элементов массива (здесь float). Другими словами, при использовании указателей для работы с массивами используется другая, отличная от известной с детского сада арифметика. Значение переменной указателя увеличивается не на k, как для обычных переменных, а на k*sizeof(тип). То есть записанное нами выражение система корректирует. Например, пусть p содержит значение адреса, условно равное 5000. Тогда в результате операции p+3 получим не 5003, а 5000+3*4=5012, где 4 = sizeof(float) — количество байт, занимаемое одним вещественным числом типа float. Другими словами, если в p был адрес начала массива (p=a), то p+3 — это адрес элемента a[3] (обозначается &a[3]), a *(p+3) — элемент массива a[3]. Если это изменённое значение адреса присвоить другой переменной, то есть выполнить q=p+3, то, как и для обычных переменных, адрес в p не изменится, а в q будет адрес элемента a[3]. Если выполнить p+=3, то значение переменной p изменяется и в нейбудет &a[3], а доступ к a[3] можно осуществить с помощью операции разыменование. Поэтому cout<<(*p); выведет значение 3-го элемента массива (a[3]).
|
|
При к=1 получаем операцию инкремент для адреса ( p++;), которая выполняется аналогично. В результате численное значение указателя увеличивается не на один байт, а на величину sizeof(тип) байт, где тип — тип элементов массива, а тип * — тип указателя. В нашем случае мы сместимся на 4 байта вправо к соседнему элементу массива с большим адресом. При организации цикла эта операция играет ту же роль, что и увеличесие индекса на единицу, например, i++;.
Определена и операция вычитание целого положительного числа из указателя, например, p-k. Числовое значение указателя уменьшается на величину k*sizeof(тип). Поэтому для нашего примера после выполнения p-=3; мы “сместимся” на 3 элемента влево, то есть адрес в переменной p уменьшится на 3*4 =12 байт. После операции декремент (p--;) указатель “перемещается” влево к соседнему элементу с меньшим адресом.
Операции p+k, p-k, p++, p-- теоретически разрешены не только для массивов. Изменяя значение указателя, можно перемещаться по участкам памяти и получать доступ к разным переменным. Но не следует надеяться, что они будут размещаться в памяти подряд в соответствии с размещением их описаний в тексте программы. Потому эти операции могут дать непредсказуемые результаты, если их использовать не для массива. Полную уверенность в последовательном размещении элементов можно получить, если используем массив.
|
|
Кроме вычитания целого числа из указателя можно вычитать один указатель из другого. Эта операция имеет смысл только тогда, когда оба указателя указывают на элементы одного и того же типа. Более того, эти элементы должны находиться в одном и том же массиве. В результате получаем количество элементов указанного типа (а не количество байт), находящихся между указателями. При этом может получиться как положительное, так и отрицательное значение. Пусть в p находится адрес начала массива (например, 5000), а в q — &a[3], то есть, 5012. Тогда cout<<(q-p); выведет не 12, а число 3 ((5012-5000)/4), а cout<<(p-q); выведет отрицательное число -3, так как p указывает на элемент с меньшим адресом.
В случае указателей на символы при работе со строками рассмотренные выше арифметические операции упрощаются, так как sizeof(char)=1, и мы имеем дело с обычной арифметикой. В этом случае, например, операция p+k “перемещает нас” на k байт, или на k символов вправо, если k>0, и влево, если k<0.
Другие арифметические операции к указателям неприменимы. Нельзя умножать, делить и складывать указатели, нельзя умножать и делить их не только на вещественное, но и на целое число. Нельзя применять битовый сдвиг и другие битавые операции к указателям, нельзя к указателю добавлять или вычитать из указателей вещественные значения.