Адресная арифметика языка Си включает в себя следующие арифметические операции над указателями.
Другие арифметические операции над указателями недопустимы.
Константа NULL объявляется в файле <stdio.h>. Нулевой указатель часто используется для организации сложных конфигураций данных, например, связанных списков и деревьев. Другой пример использования нулевого указателя: результат выполнения возвращающих указатель функций, сообщающий о неудаче.
Чтение или запись по адресу 0 в языке Си не разрешены и могут быть причиной аварийного завершения программы.
При использовании адресной арифметики важно использовать указатели с правильным типом. Если указатель будет использован для хранения адресов целых типа int, он должен быть объявлен как указатель на int. Если указатель будет содержать адреса объектов типа double, он должен быть объявлен как указатель на double, и т.д.
В языке Си последовательный доступ к данным через указатели осуществляется очень просто путем использования возможностей, предоставляемых адресной арифметикой. Когда целое число прибавляется к указателю (или вычитается из него), оно предварительно умножается на размер объекта (в байтах), на который ссылается указатель.
Пример 1
int a, *p_int;
...
char ch, *p_char;
p_int = &a;
p_char = &ch;
p_int++;
p_char++;
Пусть переменная a имеет адрес 500, а переменная ch — 520. Тогда после инициализации значением указателя p_int будет 500, а p_char соответственно 520. Операция инкрементации применительно к указателям увеличит p_int на 4, а p_char на 1.
Адресную арифметику особенно удобно использовать при обработке массивов.
Типичные приемы работы с указателями
В примерах приводятся три (из множества) возможных варианта функции strlen(), которая возвращает количество символов в строке вплоть до признака конца строки '\0' , но не включая его.
Пример 2
int strlen (register const char *s)
{
register char *p;
p = s;
while (*p != '\0')
   p++;
return p-s;
}
Этот вариант функции наиболее нагляден.
Пример 3
int strlen (register const char *s)
{
register char *p;
p = s;
while(*p)
   p++;
return p-s;
}
Данный вариант равносилен предыдущему, т.к. выражение *p будет истинно, пока его значение не нулевой символ (не символ '\0').
Пример 4
int strlen (register const char *s)
{
register char *p;
p = s;
while (*p++)
  ;
return (p-s)-1;
}
В данном варианте используется выражение, применяемое особенно часто: *p++. Так как унарные операции право-ассоциативные (связываются с операндами справа налево), это выражение интерпретируется как *(p++). Это значит, что будет увеличен указатель p, но не значение выражения *p. Поскольку операция инкрементации ++ употребляется в постфиксной форме (после операнда), указатель p не увеличивается, пока его значение не использовано. Общее действие состоит в использовании ссылки *p и последующем увеличении указателя p. В операторе возврата из (p-s) вычитается 1. Это делается потому, что после инкрементации p указывает на следующий за нулевым символом байт.