(Предыстория: у меня есть некоторый опыт внедрения компиляторов C и C ++.)
Массивы переменной длины в C99 были ошибкой. Чтобы поддерживать VLA, C99 пришлось пойти на следующие уступки здравому смыслу:
sizeof x
больше не всегда является константой времени компиляции; компилятор иногда должен генерировать код для оценки sizeof
-выражения во время выполнения.
Разрешение двумерных VLA (int A[x][y]
) потребовало нового синтаксиса для объявления функций, которые принимают 2D VLA в качестве параметров: void foo(int n, int A[][*])
.
Менее важно в мире C ++, но чрезвычайно важно для целевой аудитории C программистов встроенных систем, объявление VLA означает перебор произвольно большого фрагмента вашего стека. Это гарантированное переполнение стека и сбой. (Каждый раз, когда вы объявляете int A[n]
, вы неявно утверждаете, что у вас есть 2 ГБ свободного стека. В конце концов, если вы знаете, что «n
здесь определенно меньше 1000», вы просто объявляете int A[1000]
. Подставляя 32-битное целое число n
ибо 1000
- это признание того, что вы понятия не имеете, каким должно быть поведение вашей программы.)
Хорошо, теперь перейдем к разговору о C ++. В C ++ существует такое же сильное различие между «системой типов» и «системой ценностей», что и в C89… но мы действительно начали полагаться на нее так, как этого не делал C. Например:
template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s; // equivalently, S<int[n]> s;
Если бы n
не был константой времени компиляции (то есть, если бы A
был переменно изменяемого типа), тогда что же, черт возьми, было бы типом S
? Будет ли тип S
также определяться только во время выполнения?
Как насчет этого:
template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);
Компилятор должен сгенерировать код для некоторого экземпляра myfunc
. Как должен выглядеть этот код? Как мы можем статически сгенерировать этот код, если мы не знаем тип A1
во время компиляции?
Хуже того, что, если во время выполнения окажется, что n1 != n2
, так что !std::is_same<decltype(A1), decltype(A2)>()
? В этом случае вызов myfunc
не должен даже компилироваться, потому что определение типа шаблона не сработает! Как мы могли бы эмулировать такое поведение во время выполнения?
По сути, C ++ движется в направлении принятия все большего и большего числа решений в время компиляции: генерация кода шаблона, constexpr
оценка функции и так далее. Между тем, C99 был занят переносом традиционных решений времени компиляции (например, sizeof
) в среду выполнения. Имея это в виду, действительно ли имеет смысл прилагать какие-либо усилия, пытаясь интегрировать VLA в стиле C99 в C ++?
Как уже отмечал каждый другой ответчик, C ++ предоставляет множество механизмов распределения кучи (std::unique_ptr<int[]> A = new int[n];
или std::vector<int> A(n);
- очевидные), когда вы действительно хотите передать идею: «Я не знаю, сколько оперативной памяти мне может понадобиться». А C ++ предоставляет изящную модель обработки исключений для решения неизбежной ситуации, когда объем необходимой вам оперативной памяти превышает объем имеющейся у вас оперативной памяти. Но, надеюсь, этот ответ дает вам хорошее представление о том, почему VLA в стиле C99 не хорошо подходят для C ++ - и даже не подходят для C99. ;)
Для получения дополнительной информации по теме см. N3810 "Альтернативы для Array Extensions », статья Бьярна Страуструпа по VLA в октябре 2013 года. Точка зрения Бьярна сильно отличается от моей; N3810 больше фокусируется на поиске хорошего синтаксиса C ++ для вещей и на недопущении использования необработанных массивов в C ++, тогда как я больше сосредоточился на последствиях для метапрограммирования и системы типов. Я не знаю, считает ли он последствия метапрограммирования / типизации решенными, разрешимыми или просто неинтересными.
Хорошая запись в блоге, затрагивающая многие из этих вопросов, - «Законное использование массивов переменной длины» (Крис Веллонс, 27.10.2019).
person
Quuxplusone
schedule
03.02.2014
dynarray
(std::array
в их необработанном массиве), но оба были проголосованы аут (причем последний переводится в TS). По-видимому,dynarray
должен был сочетаться со специальной магией компилятора, чтобы при использовании в стеке его можно было оптимизировать так, чтобы он был таким же эффективным, как массив размера времени выполнения (по крайней мере, на платформах с традиционной настройкой стека и кучи. ). Я не совсем разбираюсь в деталях. - person Justin Time - Reinstate Monica   schedule 05.02.2017std::experimental::dynarray
в заголовке<experimental/dynarray>
как частьlibc++
. И Clang, и GCC поддерживают VLA на C, причем GCC (но не Clang) также позволяет их инициализировать; обратите внимание, что они идут с ограничением C, чтоsizeof
оценивается во время выполнения для VLA. - person Justin Time - Reinstate Monica   schedule 05.02.2017-Werror=vla
(GCC: включить в файл спецификации). - person Aconcagua   schedule 10.04.2019-pedantic
выдаст предупреждение об их использовании в GCC / Clang. - person user2023370   schedule 09.02.2021