Почему int x[n] не так, где n - это константное значение?

Я не могу понять, почему это неправильно

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

даже если n это уже постоянное значение.

При этом, похоже, это правильно для компилятора GNU:

const int n = 5;
int x[n]; /*without initialization*/

Мне известна функция VLA в C99, и я думаю, что она связана с тем, что происходит, но мне просто нужно кое-что прояснить, что происходит в фоновом режиме.

5 ответов

Решение

Главное, что нужно помнить, это то, что const и "постоянный" означает две совершенно разные вещи.

const Ключевое слово действительно означает "только для чтения". Константа - это числовой литерал, такой как 42 или же 1.5 (или перечисление или символьная константа). Константное выражение - это особый вид выражения, которое может быть оценено во время компиляции, например: 2 + 2,

Итак, с учетом декларации:

const int n = 5;

выражение n относится к значению объекта и не рассматривается как константное выражение. Типичный компилятор оптимизирует ссылку на n, заменяя его тем же кодом, который он использовал бы для литерала 5, но это не обязательно - и правила того, является ли выражение постоянным, определяются языком, а не хитростью текущего компилятора.

Пример разницы между const (только для чтения) и константа (оценивается во время компиляции):

const size_t now = time(NULL);

const ключевое слово означает, что вы не можете изменять значение now после его инициализации, но значение time(NULL) явно не может быть вычислено до времени выполнения.

Итак, это:

const int n = 5;
int x[n];

не более действительным в C, чем это было бы без const ключевое слово.

Язык может (и ИМХО, вероятно, должен) оценить n как постоянное выражение; это просто не определено таким образом. (У C++ есть такое правило; см. Стандарт C++ или достойную ссылку для подробностей.)

Если вы хотите именованную константу со значением 5, самый распространенный способ - определить макрос:

#define N 5
int x[N];

Другой подход заключается в определении константы перечисления:

enum { n = 5 };
int x[n];

Константы перечисления являются константными выражениями и всегда имеют тип int (что означает, что этот метод не будет работать для других типов, кроме int). И это, возможно, злоупотребление enum механизм.

Начиная со стандарта 1999 года, массив может быть определен с непостоянным размером; это VLA или массив переменной длины. Такие массивы разрешены только в области видимости блока и могут не иметь инициализаторов (поскольку компилятор не может проверить, что инициализатор имеет правильное количество элементов).

Но учитывая ваш оригинальный код:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

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

int x[] = { 1,1,3,4,5 };

И тогда вы можете вычислить длину из размера массива:

const int x_len = sizeof x / sizeof x[0];

Зачем int x[n] неправильно где n это const значение?

n не является константой. const только обещаю, что n переменная "только для чтения", которая не должна изменяться во время выполнения программы.
Обратите внимание, что в C, в отличие от C++, const квалифицированные переменные не являются постоянными. Следовательно, объявленный массив является массивом переменной длины.
Вы не можете использовать список инициализаторов для инициализации массивов переменной длины.

С11-§6.7.9/3:

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

Ты можешь использовать #define или же enum делать n постоянная

#define n 5
int x[n] = { 1,1,3,4,5 };   

Если вы исчерпывающе инициализируете массив, тогда проще, безопаснее и удобнее обслуживать компилятор, чтобы определить размер массива:

int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ; 

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

Даже если n это constВы не можете использовать его для определения размера массива, если не хотите создавать VLA. Однако вы не можете использовать список инициализаторов для инициализации VLA.

Используйте макрос для создания массива фиксированного размера.

#define ARRAY_SIZE 5

int x[ARRAY_SIZE] = { 1,1,3,4,5 };

Ваш код семантически отличается от myfunc() Вот:

void myfunc(const int n) { 
    int x[n] = { 1,1,3,4,5 };
    printf("%d\n", x[n-1]);
    *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
    return ;
}

int main(){
    myfunc(4);
    myfunc(5);
    myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
    return 0;
}

Является n неужели все это константа? Как вы думаете, сколько места должно быть выделено компилятору? x[] (так как это задача компилятора)?

Как уже отмечалось, cv-квалификатор const не означает "значение, которое является постоянным во время компиляции и для всех времен после". Это означает "значение, которое локальный код не должен изменять (хотя может)".

Другие вопросы по тегам