Массивы с нулевой длиной против указателей
EDIT: видимо, некоторые из них не разрешены/изменены в разных стандартах C. Для моей собственной гипотетической выгоды, пусть притворяется, что мы используем gcc test.c
без стандартных или предупреждающих опций.
В частности, я смотрю на особенности нижнего капота. Я добавил свое понимание. Я прав?
char **c1; //Size for a pointer is allocated on the stack. sizeof(c1) == sizeof(void*)
char *c2[0]; //Nothing is allocated on the stack. sizeof(c2) == 0
Есть ли другая разница между этими двумя случаями, о которых я не знаю (помимо sizeof)?
struct a {
int i;
char c[0]; //sizeof(a) is sizeof(int)? a.c == (&i)+1?
};
Как я понимаю, это обычно используется для массивов переменной длины в конце структур. Но как насчет
struct b {
char *c[0] //sizeof(b) is 0? where does c point?
};
int j;
struct b myb; //myb.c == (&j)+1 == $esp?
Кроме того, как известен адрес массива нулевой длины, если пространство для его указателя никогда не выделяется нигде? Я предполагаю, что так же известен обычный адрес массива, но я изо всех сил стараюсь обмануть его в данный момент.
Ответы
Ответ 1
Единственный раз, когда я когда-либо видел массивы нулевой длины, которые действительно используются, - это когда вы хотите иметь структуру переменной длины.
Как в этом примере взято из здесь
struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
Вы не хотите использовать указатель в указанной выше структуре, так как вы хотите, чтобы содержимое было частью выделенной памяти структуры.
Если вы делаете sizeof в вышеприведенной структуре, он не содержит пространства для содержимого. Я не уверен, что это когда-либо превращало его в стандарт, он дает предупреждения для разных компиляторов.
Ответ 2
ISO C запрещает 0
-длинные массивы.
char **c1;
Это определяет объект c1
типа pointer-to-pointer-to-char.
char *c2[0];
Это ошибка компиляции. Не допускается. Не в C, а не на С++.
struct a {
int i;
char c[0]; //sizeof(a) is sizeof(int)? a.c == (&i)+1?
};
Ошибка, отмеченная ранее - размер массива должен быть больше нуля.
struct a {
int i;
char c[1];
};
Также известен как struct-hack. Злоупотребление в низкоуровневом коде практически для всех ОС - Linux, Windows.
C99 дает нам массив без изменений, более известный как элемент гибкого массива:
struct a {
int i;
char c[]; /* note: no size */
};
Ответ 3
Здесь вы можете получить ответы от GCC docs. Но основное внимание уделяется теме наличия массива нулевой длины в качестве последних членов структуры. Он не дает прямых ответов относительно sizeof.
(Как было выяснено другими, массивы нулевой длины не находятся в стандарте C, но они были в GNU C.)
Ответ 4
Из стандарта C99 (7.20.3), касающегося функций распределения:
Каждое такое распределение должно давать указатель на объект, не связанный с каким-либо другим объектом.
[...]
Если размер запрашиваемого пространства равен нулю, поведение определяется реализацией: либо возвращается нулевой указатель, либо поведение такое, как если бы размер был некоторым ненулевое значение, за исключением того, что возвращаемый указатель не должен использоваться для доступа к объекту.
Другими словами, в случае объявления b
в стеке без инициализатора (где b
имеет c
, объявленный как c[]
вместо c[0]
), фактический размер b
в терминах используемого пространства будет > 0, так как вы не можете получить доступ к какой-либо части b
. Если он выделяется через malloc
, он будет либо возвращен как 0
, либо как некоторая уникальная ценность, к которой невозможно получить доступ (если вы используете sizeof(b)
).
Ответ 5
Массив не может иметь нулевой размер.
ISO 9899:2011 6.7.6.2:
If the expression is a constant expression, it shall have a value greater than zero.
Вышеприведенный текст верен как для простого массива (§1), так и для VLA (§5). Это нормативный текст в стандарте C. Компилятору не разрешено реализовать его по-разному.
gcc -std = c99 -pedantic дает предупреждение для этого.