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

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

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?

Кроме того, как известен адрес массива нулевой длины, если пространство для его указателя никогда не выделяется нигде? Я предполагаю, что так же известен обычный адрес массива, но я изо всех сил стараюсь обмануть его в данный момент.

4b9b3361

Ответ 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 дает предупреждение для этого.