Размер массива C ++, зависящий от параметра функции, вызывает ошибки компиляции - PullRequest
17 голосов
/ 23 ноября 2008

У меня есть простая функция, в которой массив объявляется с размером в зависимости от параметра, который является int.

    void f(int n){
        char a[n];
    };

    int main() {
        return 0;
    }

Этот фрагмент кода прекрасно компилируется на GNU C ++ , но не на 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, без использования new / delete)

Ответы [ 7 ]

29 голосов
/ 23 ноября 2008

Что вы нашли одним из расширений компилятора Gnu для языка C ++. В этом случае Visual C ++ полностью корректен. Массивы в C ++ должны быть определены с размером, который является константным выражением во время компиляции.

В обновлении 1999 года для этого языка была добавлена ​​функция C, называемая массивами переменной длины, где это допустимо. Если вы можете найти компилятор C, который поддерживает C99, это непросто. Но эта функция не является частью стандарта C ++ и не будет добавлена ​​в следующем обновлении стандарта C ++.

В C ++ есть два решения. Первый - использовать std :: vector, второй - просто использовать оператор new []:

char *a = new char [n];

Пока я писал свой ответ, другой опубликовал предложение использовать _alloca. Я настоятельно рекомендую против этого. Вы бы просто обменяли один нестандартный, непереносимый метод на другой, в зависимости от компилятора.

9 голосов
/ 23 ноября 2008

Ваш метод выделения из стека является расширением g ++. Чтобы сделать эквивалент в MSVC, вам нужно использовать _alloca:

char *a = (char *)_alloca(n);
5 голосов
/ 23 ноября 2008

Вы используете что-то, что не является стандартом. На самом деле это стандартный C, но не C ++. Как это странно!

Объясняя немного больше, стековые массивы, измеренные во время выполнения, не являются частью C ++, но являются частью C99, последнего стандарта для C. Поэтому некоторые компиляторы получат его, а другие - нет. Я бы рекомендовал воздерживаться от его использования, чтобы избежать проблем совместимости компилятора.

Альтернативной реализацией функциональности было бы использование new и delete, как опубликовано strager.

2 голосов
/ 07 января 2010

массив переменной длины был представлен в C99. Поддерживается в gcc, но не в msvc. По словам представителя MSVC, Microsoft не планирует поддерживать эту функцию в своем компиляторе c / C ++. В этих случаях он предложил использовать std :: vector.

Обратите внимание, что C99 не требует, чтобы массив размещался в стеке. Компилятор может разместить его в куче. Однако gcc выделяет массив в стеке.

2 голосов
/ 23 ноября 2008

Вы можете использовать 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 и другими.

1 голос
/ 07 января 2010

Было бы разумно использовать vector<> вместо массива? Или, поскольку вы заменяете char *, std::string? Они хорошо работают с определением размера среды выполнения, хотя могут быть и другие причины не использовать их.

1 голос
/ 31 декабря 2008

Обычно в C (за исключением компиляторов C99, как указали другие) и C ++, если вы хотите выделить память в стеке, размер того, что вы хотите выделить, должен быть известен во время компиляции. Локальные переменные размещаются в стеке, поэтому массив, длина которого зависит от параметра функции во время выполнения, нарушает это правило. Кляйн правильно сказал, что использование оператора «new» является одним из способов решения этой проблемы:

<code>
char *a = new char [n];

'a' по-прежнему является локальной переменной, размещенной в стеке, но вместо того, чтобы быть целым массивом (который имеет переменную длину), это просто указатель на массив (который всегда имеет одинаковый размер и поэтому известен при компиляции время). Массив размещается в куче, которая обычно воспроизводит аналог стека: стек предназначен для вещей с размером, известным во время компиляции, а куча для вещей с размером, неизвестным во время компиляции.

...