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

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

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

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

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

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

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

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

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

6.3.2.3 Указатели

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

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

Макросы

NULL

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

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

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

Ответ 2

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

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

void *p = 0;

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

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

Ответ 3

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

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

Ответ 4

Основным ограничением стандарта 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 Поведение. Хотя существует значительная вероятность того, что q получит тот же самый битовый шаблон, что и p, ничто в стандарте не потребовало бы, чтобы p считался допустимым псевдонимом для q даже в этом сценарии. С другой стороны, также кажется странным говорить, что две переменные того же типа могут содержать одинаковые биты без эквивалентного их содержимого. Таким образом, хотя явно очевидно, что функции будет разрешено либо возвращать 0 вместе со значениями *pp и *qq, которые не сравниваются поразрядным образом, или 1234 вместе со значениями *pp и *qq которые сравнивают поровну поровну, стандарт, по-видимому, позволит функции вести себя произвольно, если оба malloc будут иметь побитово-эквивалентные значения.