К указателям можно применять четыре арифметических операции: ++, ––, + и –. Причем с указателями они работают не совсем так, как с обычными числами. Операция ++ (или +1) выполняется так, чтобы указатель ссылался на адрес в памяти следующего значения используемого типа данных. Если у нас есть указатель на тип int, то при увеличении этого указателя на 1, его значение увеличится на количество байт, выделяемых под тип int – то есть 4.
Например,
int i = 0, *pi = &i;
cout << "pi1 = 0x" << pi << '\n';
pi++;
cout << "pi2 = 0x" << pi << '\n';
short t;
double f = 0, *pf = &f;
cout << "pf1 = 0x" << pf << '\n';
pf++;
cout << "pf2 = 0x" << pf << '\n';
Результат:
pi1 = 0x0012FF40
pi2 = 0x0012FF44
pf1 = 0x0012FF46
pf2 = 0x0012FF4E
Здесь pi – указатель на int. И при его увеличении он должен указывать на следующее значение такого типа в памяти, т.е. адрес должен увеличиться на 4, что и произошло. Дальше в памяти располагается неиспользуемая переменная t типа short – 2 байта (если включена оптимизация, то компилятор неиспользуемую переменную просто проигнорирует). Затем с адреса 0x0012FF46 начинается переменная f, имеющая тип double. Под такой тип выделяется 8 байт, поэтому при увеличении на единицу указателя pf, его значение реально увеличивается на 8.
Таким образом, можно сказать, что если указателю прибавляется целое приращение, то оно масштабируется размером памяти, занимаемой объектом, на который ссылается указатель. То же самое верно и для вычитания из указателя.
В результате pi+i – это адрес i-го элемента после pi, причем считается, что размер всех этих i элементов равен размеру объекта, на который указывает pi.
Складывать указатели друг с другом нельзя – это бессмысленно. Но вычитание двух указателей, имеющих одинаковый тип, допускается. При этом результатом будет количество элементов типа, на который ссылаются указатели, разделяющие эти два указателя.