Почему int x [n] неверно, где n - значение const?
Я не понимаю, почему это неправильно:
const int n = 5;
int x[n] = { 1,1,3,4,5 };
хотя n
уже является значением const.
Хотя это кажется правильным для компилятора GNU:
const int n = 5;
int x[n]; /*without initialization*/
Я знаю о функции VLA на C99, и я думаю, что это связано с тем, что происходит, но
Мне просто нужно уточнить, что происходит в фоновом режиме.
Ответы
Ответ 1
Главное помнить, что const
и "constant" означают две совершенно разные вещи.
Ключевое слово 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
.
Язык может (и IMHO, вероятно, должен) оценивать n
как постоянное выражение; это просто не определено именно так. (У С++ есть такое правило, см. Стандарт С++ или достойную ссылку для деталей gory.)
Если вам нужна именованная константа со значением 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];
Ответ 2
Почему int x[n]
неверно, если n
является значением const
?
n
не является константой. const
только обещают, что n
является переменной только для чтения, которая не должна быть изменена во время выполнения программы.
Обратите внимание, что в c, в отличие от С++, const
квалифицированные переменные не являются постоянными. Следовательно, объявленный массив представляет собой массив переменной длины.
Вы не можете использовать список инициализаторов для инициализации массивов переменной длины.
С11-§6.7.9/3:
Тип инициализируемого объекта должен быть массивом неизвестного размера или полным типом объекта, который не является массивом переменной длины.
Вы можете использовать #define
или enum
, чтобы сделать n
константу
#define n 5
int x[n] = { 1,1,3,4,5 };
Ответ 3
Если вы исчерпывающе инициализируете массив, то это проще, безопаснее и удобнее обслуживать, чтобы компилятор выводил размер массива:
int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ;
Затем, чтобы изменить размер массива, вам нужно изменить количество инициализаторов, а не изменять размер и список инициализаторов для соответствия. Особенно полезно, когда есть много инициализаторов.
Ответ 4
Даже если n
является const
, вы не можете использовать его для определения размера массива, если вы не хотите создавать VLA. Однако вы не можете использовать список инициализаторов для инициализации VLA.
Используйте макрос для создания массива фиксированного размера.
#define ARRAY_SIZE 5
int x[ARRAY_SIZE] = { 1,1,3,4,5 };
Ответ 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-qualifier const
не означает "значение, которое является постоянным во время компиляции и для всех времен после". Это означает "значение, которое локальный код не должен изменять (хотя он может)".