действительный и недопустимый синтаксис для представления размера массива во время инициализации

В настоящее время я изучаю c-массив и смущен тем, что может и не может использоваться для представления размера массива во время инициализации.

Правильно ли я предполагаю

#define SIZE 5 

а также

const int SIZE = 5; 

коренным образом отличаются друг от друга?

У них есть своя разница, и один конкретный пример, который меня смущает, - это

#define SIZE 5
int arr[SIZE] = {11, 22, 33, 44, 55}; 

действительный синтаксис, но

const int SIZE = 5;
int arr[SIZE] = {11, 22, 33, 44, 55};

Недопустимый синтаксис. Хотя интересно,

const int SIZE = 5;
int arr[SIZE];

действительный синтаксис.

Какова логика, является ли конкретный синтаксис действительным или недействительным?

Ответ 1

Правильно ли я предполагаю

#define РАЗМЕР 5
а также
const int SIZE = 5;

коренным образом отличаются друг от друга?

Да ты прав.

#define - это просто текстовая замена. В принципе, препроцессор C собирается "найти и заменить" для вас во время процесса компиляции (этап обработки). В то время как объект const означает, что "вещь, хранящаяся в этом месте, не может быть изменена", что примерно эквивалентно утверждению "только для чтения".


#define РАЗМЕР 5
int arr [SIZE] = {11, 22, 33, 44, 55};

действительный синтаксис.

Это в точности эквивалентно написанию:

int arr[5] = {11, 22, 33, 44, 55}; 

сам. Компилятор просто выполняет замещающее задание для вас, когда вы используете define.


const int SIZE = 5;

int arr [SIZE] = {11, 22, 33, 44, 55};

Недопустимый синтаксис.

Это недействительно, но причина может быть более одной. Если arr имеет статическую продолжительность хранения (т. arr Объект arr жив во время выполнения программы), то он недействителен. Поскольку C требует, чтобы размер объекта массива со статической продолжительностью хранения был постоянным выражением:

Если размер представляет собой целочисленное константное выражение, а тип элемента имеет известный постоянный размер, тип массива не является массивом переменной длины; в противном случае тип массива представляет собой тип массива переменной длины.

Таким образом, следующая программа недействительна:

const int SIZE = 5;
int arr[SIZE] = {11, 22, 33, 44, 55};
int main(void)
{
}

потому что SIZE не квалифицируется как "постоянное выражение" в C. Обратите внимание, что это полностью справедливо в C++, где SIZE квалифицируется как константное выражение (здесь разные языки).

Другая причина - стандарт C не позволяет инициализировать массивы переменной длины. Если у вас есть определение внутри функции, например:

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

Поэтому, если у вас нет инициализатора, он становится действительным:

int main(void)
{
    const int SIZE = 5;
    int arr[SIZE];  /* This is OK */
}

Точно так же вы можете обойтись без const:

int main(void)
{
    int SIZE = 5;
    int arr[SIZE]; /* Thi is OK too. */
}

В обоих вышеописанных фрагментах arr является просто массивом переменной длины.

Хотя интересно,

const int SIZE = 5; int arr [SIZE];

действительный синтаксис.

Он действителен только в том случае, если он внутри функции (как указано выше) - это VLA. Но если у вас есть статическая продолжительность хранения, такая как:

const int SIZE = 5;
int arr[SIZE]; /* arr has static storage duration just as
                 all objects defined at file scope */

int main(void)
{
}

он недействителен, поскольку, как отмечено выше, SIZE не является "постоянным выражением" в C.

Так же,

int main(void)
{
    const int SIZE = 5;
    static int arr[SIZE]; /* arr has static storage duration */
}

является недопустимым по той же причине, несмотря на то, что arr находится внутри функции, поскольку arr имеет статическую продолжительность хранения.


Однако, если у вас есть:

enum {SIZE = 5};
int arr[SIZE] = {11, 22, 33, 44, 55};
 int arr2[SIZE]; /* Valid without initializer too. */

int main()
{
}

это действительно. Зачем? Поскольку константы перечислимого типа квалифицируются как "постоянные выражения" в C.

Ответ 2

Стандарт объясняет это 6.7.9p3

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

Во втором случае это VLA, но в первом случае это не так. После предварительной обработки он аналогичен int arr[5] = {..}.

В случае VLA компилятор не знает размер VLA, когда он определен, поэтому он не может проверить правильность инициализатора. Вот почему эта инициализация не допускается.

Кстати, использование const не означает, что это постоянная времени компиляции - это просто означает, что она не может быть изменена.

Также из 6.7.6.2p4 есть четкое различие в том, когда это VLA, а когда нет: -

... Если размер представляет собой целочисленное константное выражение, а тип элемента имеет известный постоянный размер, тип массива не является массивом переменной длины; в противном случае тип массива представляет собой тип массива переменной длины.

Ответ 3

#define SIZE 5 определяют SIZE целочисленное постоянное выражение. Хотя const int SIZE = 5; определяет SIZE как переменное выражение, значение которого не должно изменяться. const определитель не делает его целочисленным постоянным выражением (в c++ он делает это).

Стандарт говорит

n1570- §6.7.6.2 (p4):

[...] Если размер представляет собой целочисленное константное выражение, а тип элемента имеет известный постоянный размер, тип массива не является массивом переменной длины; в противном случае тип массива представляет собой тип массива переменной длины. [...]

Когда вы это сделаете

#define SIZE 5
int arr[SIZE] = {11, 22, 33, 44, 55}; 

он объявляет arr как массив, который не является массивом переменной длины (VLA). В то время как

const int SIZE = 5;
int arr[SIZE];

объявляет arr как массив переменной длины, потому что SIZE не является целым константным выражением, а переменным выражением. Но одно и то же объявление терпит неудачу с списком инициализаторов, и потому, что существует ограничение на VLA, они не могут быть инициализированы с использованием списков intializers.

§6.7.9 (p2 и p3):

Ни один инициализатор не должен пытаться предоставить значение для объекта, не содержащегося в инициализированном объекте.

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