Можно ли использовать константную переменную для объявления размера массива в C?

Почему следующий код выдает ошибку?

const int a = 5; int b[a]={1,2,3,4,5}; 

А также, когда я попытался скомпилировать вышеуказанный код без ключевого слова «const», я получил ту же ошибку:

 int a = 5; int b[a]={1,2,3,4,5}; 

почему это так? Какая ошибка, которую я здесь делаю?

И еще один вопрос: когда константы заменяются их фактическими значениями в коде, т.е. если я объявляю переменную say: const int x = 5; Я знаю, что память не выделяется в памяти для переменной x, но область постоянной переменной в ПЗУ содержит значение 5 и что x просто заменяется значением 5, где x появляется в коде. Но когда это происходит? Время компиляции? Время загрузки? Время предварительной обработки?

PS: Я говорю о Embedded C (работает на микроcontrollerе и т. Д.), А не C, запущенном на моем рабочем столе. Таким образом, встроенная система должна иметь ROM (Flash, EEPROM …). Что будет тогда?

Это просто ограничение языка. Размеры статически ограниченных массивов должны быть постоянными выражениями , и, к сожалению, в C это только что-то вроде литеральной константы или выражения sizeof или подобного типа, но не константной переменной.

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

Вам может быть интересно услышать, что в C ++ разные правила, где static const int действительно является постоянным выражением, а C ++ 11 даже добавляет новое ключевое слово constexpr , чтобы обеспечить еще более общее использование константных выражений, которые охватывают больше вещи, значение которых «можно разумно определить во время компиляции».

В C const является неправильным для чтения только для чтения . const переменные могут изменять свое значение, например, вполне нормально объявлять

 const volatile int timer_tick_register; /* A CPU register. */ 

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

2 основных альтернативы VLA: enum и macros

С enum :

 enum N { N = 5 }; int is[N]; 

Это работает, потому что члены enums являются постоянными выражениями: может ли член enums быть размером массива в ANSI-C?

С макросами:

 #define N 5 int is[N]; 

Преимущество enum состоит в том, что enum имеет область видимости и является частью этапа компиляции, поэтому они также могут приводить к лучшим сообщениям об ошибках.

Преимущество макросов заключается в том, что у вас больше контроля над типом констант (например, #define N 1 vs #define N 1u ), а enum привязаны к определенному типу реализации: Является ли sizeof (enum) == sizeof ( int), всегда? Но в этом случае это не имеет большого значения.

Зачем избегать VLA

  • не находятся на C89, и только необязательно в C11
  • могут нанести накладные расходы: есть ли накладные расходы для использования массивов переменной длины? (вероятно, мало)

EDIT: просто прочитайте в wikipedia, что C11 отнесла массивы переменной длины к дополнительной функции 🙁 Doh! Первая половина сообщения может быть не такой полезной, но вторая половина отвечает на некоторые ваши другие вопросы 🙂

В дополнение к сообщению Kerrek SB, C99 (ISO / IEC 9899: 1999) имеет концепцию массива переменной длины . Стандарт дает следующий пример:

 #include  size_t fsize3(int n) { char b[n+3]; // variable length array return sizeof b; // execution time sizeof } 

Оператор sizeof расширяется следующим образом:

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

Еще один приятный пример можно найти в википедии .

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

Что касается некоторых ваших других вопросов:

Q: Когда константы заменяются их фактическими значениями в коде?

Если константа является константной переменной, она никогда не может быть «заменена» и всегда доступна как область памяти. Это связано с тем, что адрес-оператор & все еще должен работать с переменной. Однако, если адрес переменной никогда не используется, он может быть «заменен» и не иметь выделенной памяти. Из стандарта C:

Реализация может поместить объект const, который не является изменчивым в области хранения только для чтения. Более того, реализация не должна выделять хранилище для такого объекта, если его адрес никогда не используется.

Следующий вопрос…

Q: Я знаю, что память не выделяется в ОЗУ для переменной x, но область постоянной переменной в ПЗУ содержит значение 5

Это зависит от вашей системы. Если у вас есть ПЗУ, и компилятор знает, где находится ПЗУ, то он вполне может быть помещен в ПЗУ. Если нет ПЗУ, то единственным выбором, который может быть у компилятора (ну, собственно, линкер), является RAM.

Q: x просто заменяется значением 5, где x появляется в коде. Но когда это происходит? Время компиляции? Время загрузки? Время предварительной обработки?

Как уже отмечалось, это зависит от того, как используется константа. Если адрес константной переменной никогда не используется, а компилятор достаточно умен, то во время усложнения. В противном случае «замена» никогда не возникает, и это значение с местоположением в памяти; в этом случае размещение переменной в памяти происходит во время соединения. Это не произойдет во время предварительной обработки.

Может быть, стоит упомянуть, здесь вы можете использовать

 int b[] = {1, 4, 5}; 

Если вам понадобится количество элементов

  size_t sz = sizeof(b)/sizeof(b[0]); 

Я считаю, что дело до цепочки инструментов, чтобы решить, где хранить константы, флеш или оперативную память

  • Где задано свойство длины массива?
  • Как построить объект std :: array с именем инициализатора?
  • Столбец Access Array в Spark
  • Реверсирование массива в Java
  • Реверсивный массив C ++
  • Доступ к массивам по индексу в C и C ++
  • 1D или 2D, что быстрее?
  • Как удалить определенный элемент из JSONArray?
  • Теперь, когда у нас есть std :: array, какие применения остаются для массивов в стиле C?
  • Как изменить массив int в Java?
  • jQuery анализирует multidimensional array JSON
  • Давайте будем гением компьютера.