Размер массива C++, зависящий от параметра функции, вызывает ошибки компиляции
-
10-07-2019 - |
Вопрос
У меня есть простая функция, в которой массив объявляется с размером в зависимости от параметра, который INT.
void f(int n){
char a[n];
};
int main() {
return 0;
}
Этот фрагмент кода отлично компилируется на ГНУ С++, но не на MSVC 2005.
Я получаю следующие ошибки компиляции:
.\main.cpp(4) : error C2057: expected constant expression
.\main.cpp(4) : error C2466: cannot allocate an array of constant size 0
.\main.cpp(4) : error C2133: 'a' : unknown size
Что я могу сделать, чтобы это исправить?
(Я заинтересован в том, чтобы эта работа работала с MSVC без использования нового/удаления)
Решение
Вы нашли это одно из расширений компилятора Gnu к языку C++.В этом случае Visual C++ совершенно прав.Массивы в C++ должны определяться с размером, который является константным выражением времени компиляции.
В C в обновлении этого языка 1999 года была добавлена функция, называемая массивами переменной длины, где это разрешено.Если вы сможете найти компилятор C, поддерживающий C99, это непросто.Но эта функция не является частью стандарта C++ и не будет добавлена в следующем обновлении стандарта C++.
В C++ есть два решения.Первый — использовать std::vector, второй — просто использовать оператор new []
:
char *a = new char [n];
Пока я писал свой ответ, другой опубликовал предложение использовать _alloca.Я бы настоятельно рекомендовал против этого.Вы просто замените один нестандартный, непереносимый метод на другой, столь же специфичный для компилятора.
Другие советы
Ваш метод выделения из стека является расширением g ++. Чтобы сделать эквивалент в MSVC, вам нужно использовать _alloca:
char *a = (char *)_alloca(n);
Вы используете что-то, что не является стандартом. На самом деле это стандартный C, но не C ++. Как это странно!
Объясняя немного больше, стековые массивы размера во время выполнения не являются частью C ++, но являются частью C99, последнего стандарта для C. Это & # 180, поэтому некоторые компиляторы получат его, а другие - нет. , Я бы порекомендовал воздержаться от его использования, чтобы избежать проблем совместимости компилятора.
Альтернативной реализацией функциональности было бы использование new и delete в соответствии с публикацией strager.
Вы можете использовать new / delete для выделения / освобождения памяти в куче. Это медленнее и, возможно, более подвержено ошибкам, чем использование char [n], но это, к сожалению, пока не является частью стандарта C ++.
Вы можете использовать класс массива boost для исключительного безопасного метода использования new []. delete [] автоматически вызывается для a , когда выходит из области видимости.
void f(int n) {
boost::scoped_array<char> a(new char[n]);
/* Code here. */
}
Вы также можете использовать std :: vector и резервировать () несколько байтов:
void f(int n) {
std::vector<char> a;
a.resize(n);
/* Code here. */
}
Если вы делаете хотите использовать char [n], скомпилируйте его как код C99 вместо кода C ++.
Если по какой-то причине вам абсолютно необходимо разместить данные в стеке, используйте _alloca или _malloca / _freea, которые являются расширениями, предоставляемыми библиотеками MSVC и т. д.
массив переменной длины был представлен в C99. Поддерживается в gcc, но не в msvc. По словам человека из команды MSVC, Microsoft не планирует поддерживать эту функцию в своем компиляторе c / C ++. Он предложил использовать std :: vector в этих случаях.
Обратите внимание, что C99 не требует, чтобы массив размещался в стеке. Компилятор может разместить его в куче. Однако gcc выделяет массив в стеке.
Обычно в C (за исключением компиляторов C99, как указывали другие) и C++, если вы хотите выделить память в стеке, размер того, что вы хотите выделить, должен быть известен во время компиляции.Локальные переменные размещаются в стеке, поэтому массив, длина которого зависит от параметра функции во время выполнения, нарушает это правило.Кляйн прав, отмечая, что использование оператора new — один из способов решения этой проблемы:
char *a = new char [n];
'a' по-прежнему является локальной переменной, размещенной в стеке, но вместо всего массива (имеющего переменную длину) это просто указатель на массив (который всегда имеет одинаковый размер и, следовательно, известен во время компиляции).Массив размещается в куче, которая обычно является аналогом стека: стек предназначен для объектов, размер которых известен во время компиляции, а куча — для объектов, размер которых неизвестен во время компиляции.
Было бы разумно использовать vector<>
, а не массив? Или, поскольку вы заменяете char *
, std::string
? Они хорошо работают с определением размера среды выполнения, хотя могут быть и другие причины не использовать их.