Стандарт определяет константу нулевого указателя, чтобы все биты были установлены на ноль?

(Я цитирую ISO / IEC 9899: 201x)

Здесь мы видим, что целочисленное константное выражение имеет целочисленный тип:

6.6. Постоянные выражения

6. Целочисленное константное выражение должно иметь целочисленный тип и должно иметь только операнды, которые являются целыми константами, константами enums, символьными константами, sizeof выражениями, результаты которых являются целыми константами, выражениями Alignof и плавающими константами, которые являются непосредственными операндами приведений. Операторы Cast в целочисленном постоянном выражении должны преобразовывать только арифметические типы в целые типы, за исключением того, что они являются частью операнда для оператора sizeof или _Alignof.

Тогда это справедливо для любого целочисленного типа:

6.2.6.2 Целочисленные типы

5. Значения любых битов дополнений не определены. Действительное (не-ловушечное) представление объекта знакового целочисленного типа, где знаковый бит равно нулю, является допустимым представлением объекта соответствующего неподписанного типа и должно представлять одно и то же значение. Для любого целочисленного типа представление объекта, где все биты равны нулю, должно быть представлением нулевого значения в этом типе.

Затем мы видим, что константа нулевого указателя определяется с использованием целочисленного константного выражения со значением 0.

6.3.2.3 Указатели

3. Целочисленное константное выражение со значением 0 или такое выражение, отлитое от типа void *, называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнится неравномерно с указателем на любой объект или функцию.

Поэтому константа нулевого указателя должна иметь все биты, установленные на ноль.

Но есть много ответов в Интернете и на StackOverflow, которые говорят, что это не так.

Мне тяжело полагать, что им дали цитаты.

(Пожалуйста, ответьте, используя ссылки на последний стандарт)

Нет, NULL не обязательно должен быть бит.

N1570 6.3.2.3 Указатели пункт 3:

Целочисленное константное выражение со значением 0 или такое выражение, отлитое от типа void *, называется константой нулевого указателя. 66) Если константа нулевого указателя преобразуется в тип указателя , результирующий указатель, называемый нулевым указателем, гарантированно сравнится неравномерно с указателем на любой объект или функцию.

См. Мой акцент выше: Integer 0 преобразуется при необходимости, он не должен иметь такую ​​же представление битов.

Примечание 66 в нижней части страницы гласит:

66) Макрос NULL определяется в (и других заголовках) как константа нулевого указателя; см. 7.19.

Это приводит нас к абзацу этой главы:

Макросы

НОЛЬ

который расширяется до постоянной константы указателя на реализацию

Более того, в Приложении J.3.12 («Проблемы с переносимостью, поведение, определяемое реализацией, функции библиотеки»):

– Константа нулевого указателя, к которой расширяется макрос NULL (7.19).

Стандарт определяет константу нулевого указателя, чтобы все биты были установлены на ноль?

Нет, нет. Никакой пункт Стандарта C не налагает такого требования.

 void *p = 0; 

p например, является нулевым указателем, но стандарт не требует, чтобы объект p должен был иметь все бит.

Для информации сайт c-faq упоминает некоторые системы с ненулевыми представлениями нулевого указателя здесь: http://c-faq.com/null/machexamp.html

Спросить о представлении константы нулевого указателя совершенно бессмысленно.

Константа нулевого указателя либо имеет целочисленный тип, либо тип void *. Как бы то ни было, это значение . Это не объект. Значения не имеют представления, только объекты имеют. Мы можем говорить только о представлениях, беря адрес объекта, отбрасывая его на char * или unsigned char * и глядя на байты. Мы не можем сделать это с константой нулевого указателя. Как только он назначается объекту, он больше не является константой нулевого указателя.

Основное ограничение в стандарте C состоит в том, что, поскольку авторы хотят избежать запрещения компиляторам вести себя каким-либо образом, над которым какой-либо производственный код мог бы опираться, он не может указать много вещей, которые должны знать программисты. Как следствие, часто необходимо делать предположения о вещах, которые не указаны стандартом, но соответствуют поведению обычных компиляторов. Тот факт, что все байты, содержащие нулевой указатель, равен нулю, является одним из таких предположений.

Ничто в стандарте C не указывает ничего о представлении любого уровня на уровне бит, за исключением того, что каждое возможное значение каждого типа данных, включая указатели, будет отображаться как последовательность значений char (*). Тем не менее, почти на всех платформах платформы, обнуляющих все байты, связанные со структурой, эквивалентно установке всех членов в статические значения по умолчанию для их типов (значение по умолчанию для нулевого указателя). Кроме того, код, который использует calloc для получения обнуленного блока ОЗУ для коллекции структур, часто будет намного быстрее, чем код, который использует malloc а затем должен вручную очистить каждого члена каждой структуры или использовать calloc и все же вручную очищает каждый нецелый член каждой структуры.

Поэтому я бы предположил, что во многих случаях вполне разумно писать код, предназначенный для диалектов C, где нулевые указатели хранятся как all-bytes-zero, и имеют в качестве документального требования, что он не будет работать на диалектах, где это не случай. Возможно, когда-нибудь ИСО предоставит стандартное средство, с помощью которого такие требования могут быть задокументированы в машиночитаемой форме (чтобы каждый компилятор должен был либо соблюдать заявленные требования программы, либо отказаться от компиляции), но пока я еще ничего не знаю существует.

(*) Насколько я понимаю, возникает вопрос, нужны ли компиляторы для выполнения этого предположения. Рассмотрим, например:

 int funcomp(int **pp, int **qq) { int *p,*q; p = (int*)malloc(1234); *pp = p; free(p); q = (int*)malloc(1234); *qq = q; *q = 1234; if (!memcmp(pp, qq, sizeof p)) return *p; return 0; } 

После free(p) любая попытка доступа к *p будет Undefined Behavior. Хотя существует большая вероятность того, что q получит тот же самый бит-шаблон, что и p , ничто в стандарте не потребует, чтобы p считался допустимым псевдонимом для q даже в этом сценарии. С другой стороны, также кажется странным говорить, что две переменные того же типа могут содержать одинаковые биты без эквивалентного их содержимого. Таким образом, хотя очевидно, что функции можно было бы либо возвращать 0 вместе со значениями *pp и *qq которые не сравниваются поразрядным образом, либо 1234 вместе со значениями *pp и *qq которые сравнивают бит – равномерно, стандарт, по-видимому, позволит функции вести себя произвольно, если оба malloc иметь побитово-эквивалентные значения.