Стандарт C не предъявляет никаких требований к поведению в этом случае, но во многих реализациях поведение арифметики указателей во многих случаях выходит за рамки минимальных требований, требуемых Стандартом, включая этот.
В любой соответствующей реализации C и почти во всех (если не во всех) реализациях C-подобных диалектов для любого указателя p
будут соблюдаться следующие гарантии, так что *p
или *(p-1)
идентифицирует некоторый объект:
- Для любого целочисленного значения
z
, равного нулю, значения указателя (p+z)
и (p-z)
будут во всех отношениях эквивалентны p
, за исключением того, что они будут постоянными только в том случае, если оба значения p
иz
являются постоянными. - Для любого
q
, который эквивалентен p
, выражения p-q
и q-p
оба приведут к нулю.
Наличие такихгарантия выполнения для всех значений указателя, включая ноль, может устранить необходимость некоторых проверок на ноль в пользовательском коде.Кроме того, на большинстве платформ генерация кода, который поддерживает такие гарантии для всех значений указателей, независимо от того, являются ли они нулевыми, будет проще и дешевле, чем специальная обработка нулей.Однако на некоторых платформах могут возникать попытки выполнить арифметику указателей с нулевыми указателями, даже при добавлении или вычитании нуля.На таких платформах число сгенерированных компилятором нулевых проверок, которые должны были бы быть добавлены к операциям с указателями для обеспечения гарантии, во многих случаях значительно превышало бы количество сгенерированных пользователем пустых проверок, которые могут быть опущены в результате.
Если бы была реализация, в которой затраты на поддержание гарантий были бы велики, но немногие, если какие-либо программы получили бы от них какую-либо выгоду, имело бы смысл позволить ей отлавливать вычисления «ноль + ноль» и требовать, чтобыПользовательский код для такой реализации включает в себя ручные проверки на нуль, которые гарантии могли бы сделать ненужными.Ожидается, что такой резерв не повлияет на другие 99,44% реализаций, где стоимость поддержки гарантий будет превышать стоимость.Такие реализации должны поддерживать такие гарантии, но их авторам не нужно, чтобы авторы стандарта сообщали им об этом.
Авторы C ++ решили, что соответствующие реализации должны поддерживать вышеуказанные гарантии любой ценой, даже наплатформы, где они могут существенно снизить производительность арифметики указателей.Они посчитали, что стоимость гарантий даже на платформах, где их было бы дорого поддерживать, превысила бы стоимость.На такое отношение могло повлиять желание обращаться с C ++ как с языком более высокого уровня, чем с C. Можно ожидать, что программист AC узнает, когда конкретная целевая платформа будет обрабатывать случаи типа (ноль + ноль) необычным образом, но программисты C ++не ожидалось, что они будут заниматься такими вещами.Таким образом, гарантирование согласованной поведенческой модели было признано оправданным.
Конечно, в наши дни вопросы о том, что «определено», редко имеют какое-либо отношение к тому, какое поведение может поддерживать платформа.Вместо этого теперь модно для компиляторов - во имя «оптимизации» - требовать, чтобы программисты вручную писали код для обработки угловых случаев, которые раньше платформы обрабатывали бы правильно.Например, если код, который должен выводить n
символов, начиная с адреса p
, записывается как:
void out_characters(unsigned char *p, int n)
{
unsigned char *end = p+n;
while(p < end)
out_byte(*p++);
}
, старые компиляторы генерировали бы код, который надежно ничего не выводил бы без побочных эффектов,если p == NULL и n == 0, то нет необходимости в особом случае n == 0.Однако на более новых компиляторах придется добавить дополнительный код:
void out_characters(unsigned char *p, int n)
{
if (n)
{
unsigned char *end = p+n;
while(p < end)
out_byte(*p++);
}
}
, от которого оптимизатор может или не сможет избавиться.Отказ от включения дополнительного кода может привести к тому, что некоторые компиляторы поймут, что, поскольку p «не может быть нулевым», любые последующие проверки нулевого указателя могут быть опущены, что приведет к разрыву кода в месте, не связанном с реальной «проблемой».