(背景:我有一些实现 C 和 C++ 编译器的经验。)
C99 中的可变长度数组基本上是一个失误。为了支持 VLA,C99 不得不对常识做出以下让步:
sizeof x
不再总是编译时常量;编译器有时必须生成代码以sizeof
在运行时评估 - 表达式。
允许二维 VLA ( int A[x][y]
) 需要一种新的语法来声明将二维 VLA 作为参数的函数:void foo(int n, int A[][*])
.
在 C++ 世界中不太重要,但对于 C 的嵌入式系统程序员的目标受众来说却极为重要,声明 VLA 意味着在你的堆栈中任意大块地咀嚼。这是有保证的堆栈溢出和崩溃。(任何时候你声明int A[n]
,你都在暗示你有 2GB 的堆栈可用。毕竟,如果你知道“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
在这种情况下,甚至不应该调用compile ,因为模板类型推导应该失败!我们怎么可能在运行时模拟这种行为?
基本上,C++ 正朝着将越来越多的决策推向编译时的方向发展:模板代码生成、constexpr
函数评估等等。同时,C99 忙于将传统的编译时决策(例如sizeof
)推入运行时。考虑到这一点,花费任何精力尝试将 C99 风格的 VLA 集成到 C++ 中真的有意义吗?
正如所有其他回答者已经指出的那样,当您真正想要传达“我不知道我可能需要多少 RAM”的想法时,C++ 提供了许多堆分配机制(std::unique_ptr<int[]> A = new int[n];
或者是显而易见的机制)。std::vector<int> A(n);
C++ 提供了一个漂亮的异常处理模型来处理不可避免的情况,即您需要的 RAM 量大于您拥有的 RAM 量。但希望这个答案能让您很好地了解为什么 C99 风格的 VLA不适合 C++——甚至不适合 C99。;)
有关该主题的更多信息,请参阅Bjarne Stroustrup 2013 年 10 月关于 VLA 的论文N3810 “Alternatives for Array Extensions” 。Bjarne 的 POV 和我的很不一样;N3810 更侧重于为事物找到一种好的 C++语法,并反对在 C++ 中使用原始数组,而我更侧重于对元编程和类型系统的影响。我不知道他是否认为元编程/类型系统的影响已解决、可解决或仅仅是无趣。
一篇很好的博客文章提到了许多相同的观点,即“合法使用可变长度数组”(Chris Wellons,2019-10-27)。