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

Почему этот код C работает правильно?

Коду C нравится следующее:

 #include <stdio.h>
 #include <unistd.h> 
 #define DIM(a) (sizeof(a)/sizeof(a[0])) 
 struct obj
 {
     int a[1];
 };
 int main()
 {
     struct obj *p = NULL;
     printf("%d\n",DIM(p->a));
     return 0;
 }

Этот указатель объекта p равен NULL, поэтому я думаю, что этот p->a является незаконным. Но я тестировал этот код в Ubuntu14.04, он может выполняться правильно. Итак, я хочу знать, почему...


Примечание: исходный код имел int a[0] выше, но я изменил его на int a[1], поскольку все, кажется, зависли на этом, а не на самом деле, это:

Является ли выражение sizeof(p->a) действительным, когда p равно NULL?

4b9b3361

Ответ 1

Поскольку sizeof - это построение времени компиляции, это не зависит от оценки ввода. sizeof(p->a) оценивается только на основе объявленного типа члена p::a и становится константой в исполняемом файле. Таким образом, факт, что p указывает на нуль, не имеет значения.

Время выполнения p не играет никакой роли в выражении sizeof(p->a).

В C и С++ sizeof - это оператор, а не функция. Он может применяться либо к типу-идентификатору, либо к выражению. За исключением случая, когда выражение и выражение представляют собой массив переменной длины (новый в C99) (как указано paxdiablo), выражение является неоцененным операндом, и результат будет таким же, как если бы вы взяли sizeof вместо типа этого выражения. (C.f. C11 ссылки из-за paxdiablo ниже, рабочий документ С++ 14 5.3.3.1)

Ответ 2

Прежде всего, если вы хотите действительно портативный код, вам не следует пытаться создать массив нулевого размера 1 как вы это делали в исходном вопросе, теперь исправлено. Но поскольку это не имеет особого отношения к вашему вопросу о том, действителен ли sizeof(p->a) при p == NULL, мы можем его игнорировать на данный момент.

Из раздела C11 6.5.3.4 The sizeof and _Alignof operators (полужирный):

2/Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражением или именем в скобках типа. Размер определяется типом операнда. Результат - целое число. Если тип операнда - тип массива переменной длины, то операнд оценивается; в противном случае операнд не оценивается, а результат представляет собой целочисленную константу.

Поэтому никакая оценка операнда не выполняется, если только он не является массивом переменной длины (который не является вашим примером). Для определения размера используется только сам тип.


1 Для юристов языка там C11 заявляет в 6.7.6.2 Array declarators (мой жирный шрифт):

1/В дополнение к необязательным типам классификаторов и ключевому слову static, [ и ] могут ограничивать выражение или *. Если они ограничивают выражение (которое определяет размер массива), выражение должно иметь целочисленный тип. Если выражение является константным выражением, оно должно иметь значение больше нуля.

Однако, поскольку в разделе ограничений (где shall и shall not не связаны поведение undefined), это просто означает, что сама программа не является строго соответствующей. Он по-прежнему покрывается самим стандартом.

Ответ 3

Этот код содержит нарушение ограничений в ISO C из-за:

struct obj
{
    int a[0];
};

Массивы нулевого размера не разрешены нигде. Поэтому стандарт C не определяет поведение этой программы (хотя похоже, есть некоторые дебаты об этом).

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

Расширения должны быть задокументированы (C11 4/8), поэтому, надеюсь, ваша документация компилятора определяет его поведение для struct obj (структура с нулевым размером?) и значение sizeof p->a, и оценивает или нет sizeof его операнд, когда операнд обозначает массив нулевого размера.

Ответ 4

sizeof() не заботится о содержании чего-либо, он просто смотрит на результирующий тип выражения.

Так как C99 и variable length arrays, он вычисляется во время выполнения, когда массив переменной длины является частью выражения в операнде sizeof. В противном случае операнд не оценивается, а результат - integer constant

Zero-size array объявления в structs никогда не были разрешены никаким C standard, но некоторые старые компиляторы разрешили его, прежде чем он стал стандартным для компиляторов, чтобы разрешить incomplete array declarations with empty brackets(flexible array members).