Подтвердить что ты не робот

Как эта структура может иметь sizeof == 0?

Существует старое сообщение с просьбой построить конструкцию, для которой sizeof вернет 0. Есть несколько высокоуровневых ответов от пользователей высокой репутации, говорящих, что по стандарту ни один тип или переменная не может иметь sizeof 0. И я согласен на 100% с этим.

Однако есть этот новый ответ, в котором представлено это решение:

struct ZeroMemory {
    int *a[0];
};

Я собирался проголосовать и прокомментировать это, но время, проведенное здесь, научило меня проверять даже то, на что я на 100% уверен. Поэтому... к моему удивлению, оба gcc и clang показывают те же результаты: sizeof(ZeroMemory) == 0. Более того, sizeof переменной 0:

ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...

Whaaaat...?

ссылка Godbolt

Как это возможно?

4b9b3361

Ответ 1

До того, как C был стандартизирован, многим компиляторам не пришлось бы работать с нулевыми типами, если бы код никогда не пытался вычитать один указатель на нулевой размер из другого. Такие типы были полезны, и поддержка их была проще и дешевле, чем их запрещать. Другие компиляторы решили запретить такие типы, однако, и некоторый код статического утверждения мог полагаться на тот факт, что они будут кричать, если код попытается создать массив нулевого размера. Авторы Стандарта столкнулись с выбором:

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

  • Разрешить компиляторам молча принимать объявления массивов нулевого размера, даже в случаях, когда целью таких заявлений было бы инициирование диагностики и прерывания, а также позволяют компиляторам встречаться с такими декларации, чтобы либо прекратить компиляцию, либо продолжить ее на досуге.

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

Авторы Стандарта выбрали №3. Следовательно, объявления массивов нулевого размера рассматриваются стандартным "расширением", хотя такие конструкции широко поддерживались до того, как стандарт запретил их.

Стандарт С++ допускает существование пустых объектов, но для того, чтобы разрешить адреса пустых объектов использоваться в качестве токенов, он требует, чтобы они имели минимальный размер 1. Для объекта, у которого нет членов, поэтому размер 0 будет нарушать Стандарт. Однако, если объект содержит элементы с нулевым размером, стандарт С++ не предъявляет требований о том, как он обрабатывается, за исключением того, что программа, содержащая такое объявление, должна инициировать диагностику. Поскольку большинство кодов, которые используют такие объявления, ожидают, что результирующие объекты имеют нулевой размер, наиболее полезным поведением для компиляторов, получающих такой код, является их обработка таким образом.

Ответ 2

Как указано Jarod42 массивы нулевого размера не являются стандартными С++, а расширениями GCC и Clang.

Добавление -pedantic вызывает это предупреждение:

5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
    int *a[0];
           ^

Я всегда забываю, что std=c++XX (вместо std=gnu++XX) не отключает все расширения.

Это все еще не объясняет поведение sizeof. Но, по крайней мере, мы знаем, что это не стандарт...

Ответ 3

В С++ массив нулевого размера является незаконным.

ISO/IEC 14882: 2003 8.3.4/1:

[..] Если присутствует постоянное выражение (5.19), оно должно быть интегральным постоянным выражением, а его значение должно быть больше нуля. Константное выражение определяет границу (количество элементов в) массива. Если значение константного выражения равно N, массив имеет N элементы с номером 0 до N-1, а тип идентификатора D - это массив производных-деклараторов типа N T ". [..]

g++ требует, чтобы флаг -pedantic выдавал предупреждение в массиве нулевого размера.

Ответ 4

Массивы с нулевой длиной являются расширением GCC и Clang. Применение sizeof к массивам нулевой длины оценивается до нуля.

Класс С++ (пустой) не может иметь размер 0, но обратите внимание, что класс ZeroMemory не пуст. Он имеет именованный элемент с размером 0 и применение sizeof возвращает ноль.