Адресная арифметика (address arithmetic) — это способ вычисления адреса какого-либо объекта при помощи арифметических операций над указателями, а также использование указателей в операциях сравнения. Адресную арифметику также называют арифметикой над указателями (pointer arithmetic).
Согласно стандартам языков С и С++, при арифметике с указателями результирующий адрес должен оставаться строго на границе единичного объекта массива (или следовать сразу за ним). Сложение или вычитание указателя сдвигает его на величину, кратную размеру того типа данных, на который он указывает. Например, есть указатель на массив целых чисел (по 4 байта на каждый элемент). Инкремент этого указателя приведет к увеличению его значения на 4 (размер элемента). Этой особенностью часто пользуются, чтобы получить N-ый элемент в последовательном контейнере.
int array[10] {};
int *ptr = array; // points to the first element
int *next = ptr + 1; // points to the second element
....
int *next = ptr + 9; // points to the last element
Арифметика с указателями не может быть применена к указателям на неполные типы, поскольку их размер неизвестен. Компилятор не будет знать, на какое количество байт необходимо сместить указатель.
struct SomeType; // Incomplete type
....
SomeType* ptr = ....;
SomeType* next = ptr + 1; // ERROR: 'SomeType *': unknown size
Однако существуют нестандартные расширения компилятора, позволяющие выполнять байтовую арифметику на нетипизированных указателях (void *).
Указатели и целочисленные переменные не являются взаимозаменяемыми объектами. Константа нуль — единственное исключение из этого правила: её можно присвоить указателю, который можно сравнить с нулевой константой. Чтобы показать, что нуль — это специальное значение для указателя, вместо цифры 0, как правило, записывают константу 'NULL'. Начиная с С23 / C++11, для этих целей лучше использовать nullptr.
Адресная арифметика даёт программисту возможность работать с разными типами одинаковым способом: за счёт сложения и вычитания необходимого числа элементов вместо действительного сдвига байтов. В частности, описание языка С явно задаёт равнозначность синтаксической структуры A[i], которая является i-ым элементом массива A, и *(A + i), которая представляет собой содержимое элемента, на который указывает выражение (A + i). Также подразумевается, что i[A] равнозначно A[i]. Другими словами, все четыре указателя из примера ниже указывают на один и тот же элемент, просто разными способами:
int *a = array + 2;
int *b = 2 + array;
int *c = array[2];
int *d = 2[array];
Адресная арифметика является фундаментальной концепцией языков С и С++, поэтому широко используется при работе с массивами, строками и динамически выделенной памятью. При использовании адресной арифметики необходимо соблюдать осторожность и следить за корректностью вычислений, чтобы избежать выхода за границы массива или доступа к недопустимым областям памяти.
Из-за сложностей использования указателей многие современные языки программирования высокого уровня (например, Java или C#) не разрешают прямой доступ к памяти с использованием адресов.
Список источников
0