Почему массив с нулевой длиной разрешен только в том случае, если его куча выделена?
Я замечаю, что ему не разрешено создавать массивы, не содержащие кучи нулевой длины.
// error: cannot allocate an array of constant length zero
char a[0];
Я также заметил, что он разрешил создавать массивы, выделенные кучей нулевой длины.
// this is okay though
char *pa = new char[0];
Я думаю, они оба гарантированы Стандартом (у меня нет копии Стандарта под рукой). Если да, то почему они такие разные? Почему бы просто не разрешить массив нулевой длины в стеке (или наоборот)?
Ответы
Ответ 1
Массив длины 0 не очень полезен. Когда вы вычисляете
измерение, это может произойти, и полезно не рассматривать дело
особенно в вашем коде. Вне new
размерность массива
должен быть постоянным, а не рассчитанным значением, и если вы знаете, что
константа равна 0, зачем определять ее?
По крайней мере, это объяснение, которое я слышал (от людей, которые работали над
Это). Я не полностью убежден: это не редкость в коде, чтобы
работа с символическими константами, значение которых неизвестно (из заголовка
файла или даже командной строки). Поэтому, вероятно, было бы разумно
позволяет массивы из 0 элементов. И хотя бы один компилятор в прошлом
позволил им, хотя я забыл, что.
Частым трюком на С++ является использование такого массива во время компиляции
утверждают, что-то вроде:
char dummyToTestSomeSpecificCondition[ condition ];
Это не скомпилируется, если условие ложно и будет скомпилировано, если
это не так. За исключением одного компилятора (если он все еще существует); Я буду использовать
что-то вроде:
char dummyToTestSomeSpecificCondition[ 2 * condition - 1 ];
на всякий случай.
Ответ 2
Это рассматривается в следующих разделах стандарта С++.
3.7.3.1/2:
[32. Цель состоит в том, чтобы реализовать оператор new()
, вызвав malloc()
или calloc()
, поэтому правила практически одинаковы. С++ отличается от C, требуя нулевого запроса, чтобы вернуть ненулевой указатель.]
А также
5.3.4, пункт 7
Когда значение выражения в direct-new-declarator равно нулю, функция распределения вызывается для выделения массива без элементов.
Массив размера 0 не допускается стандартом С++:
8.3.4/1:
"Если присутствует _constant-expression+
(5.19), он должен быть интегральным постоянным выражением, и его значение должно быть больше нуля."
В моем понимании обоснованием этого является тот факт, что стандарт С++ требует, чтобы каждый объект имел уникальный адрес (именно по этой причине даже пустой объект класса имеет размер 1). В случае отсутствия heap zero size array, не нужно создавать объекты, и, следовательно, для этого не требуется адрес, и, следовательно, нет необходимости разрешать его на первом месте.
Что касается c, то массивы нулевой длины разрешены стандартом c, как правило, они используются для реализации структур, имеющих переменный размер, путем размещения массива нулевой длины в конце структуры. Если моя память служит моей правильной, ее популярно называют C struct Hack.
Ответ 3
Я думаю, что главная причина, по которой они ведут себя по-другому, заключается в том, что первая:
char a[0];
- массив размера 0, и это запрещено, потому что его размер должен быть по определению 0 * sizeof (char), а в C или С++ вы не можете определить типы размера 0.
Но второй:
char *pa = new char[0];
является не реальным массивом, это всего лишь кусок 0 объектов типа char, помещенных вместе в память. Поскольку последовательность 0 объектов может быть полезна, это разрешено. Он просто возвращает указатель мимо последнего элемента, и это отлично.
Чтобы добавить к моему аргументу, рассмотрим следующие примеры:
new int[0][3]; //ok: create 0 arrays of 3 integers
new int[3][0]; //error: create 3 arrays of 0 integers
Хотя обе строки будут использовать одну и ту же память (0 байт), одна разрешена, а другая - нет.
Ответ 4
Это зависит от реализации/флагов компилятора. Visual С++ не позволяет, GCC разрешает (не знаю, как отключить).
Используя этот подход, STATIC_ASSERT может быть реализован в VC, но не в GCC:
#define STATIC_ASSERT(_cond) { char __dummy[_cond];}
Ответ 5
Даже интуитивно это имеет смысл.
Так как метод выделения кучи создает указатель на стеке, на кусок памяти, выделенный в куче, он все еще "создает что-то" размера: sizeof(void*)
. В случае выделения массива нулевой длины указатель, который существует в стеке, может указывать в любом месте, но нигде не имеет смысла.
В отличие от этого, если вы выделяете массив в стеке, как бы выглядел значимый объект массива нулевой длины? Это действительно не имеет смысла.