在过去的几年里,我不怎么使用C。当我今天读这个问题时,我遇到了一些我不熟悉的C语法。

显然,在C99中,以下语法是有效的:

void foo(int n) {
    int values[n]; //Declare a variable length array
}

这似乎是一个非常有用的功能。是否曾经讨论过将它添加到c++标准中,如果有,为什么省略了它?

一些潜在的原因:

编译器供应商难以实现 与标准的其他部分不兼容 功能可以用其他c++构造来模拟

c++标准规定数组大小必须是常量表达式(8.3.4.1)。

是的,当然我意识到在这个玩具示例中可以使用std::vector<int> values(m);,但这是从堆中分配内存而不是堆栈。如果我想要一个多维数组,比如:

void foo(int x, int y, int z) {
    int values[x][y][z]; // Declare a variable length array
}

矢量版本变得相当笨拙:

void foo(int x, int y, int z) {
    vector< vector< vector<int> > > values( /* Really painful expression here. */);
}

片、行和列也可能分布在整个内存中。

看看comp. stdc++上的讨论,很明显这个问题很有争议,争论双方都有一些重量级人物。当然,std::vector并不总是更好的解决方案。


当前回答

这样的数组是C99的一部分,但不是标准c++的一部分。正如其他人所说,向量总是一个更好的解决方案,这可能就是为什么可变大小数组不在c++标准中(或在提议的c++ 0x标准中)。

顺便说一句,关于“为什么”c++标准是这样的问题,可以去Usenet新闻组comp. stdc++。

其他回答

(背景:我有一些实现C和c++编译器的经验。)

C99中的变长数组基本上是一个错误。为了支持VLAs, C99不得不根据常识作出以下让步:

sizeof x is no longer always a compile-time constant; the compiler must sometimes generate code to evaluate a sizeof-expression at runtime. Allowing two-dimensional VLAs (int A[x][y]) required a new syntax for declaring functions that take 2D VLAs as parameters: void foo(int n, int A[][*]). Less importantly in the C++ world, but extremely important for C's target audience of embedded-systems programmers, declaring a VLA means chomping an arbitrarily large chunk of your stack. This is a guaranteed stack-overflow and crash. (Anytime you declare int A[n], you're implicitly asserting that you have 2GB of stack to spare. After all, if you know "n is definitely less than 1000 here", then you would just declare int A[1000]. Substituting the 32-bit integer n for 1000 is an admission that you have no idea what the behavior of your program ought to be.)

好了,现在让我们开始讨论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)推到运行时。考虑到这一点,将c99风格的vla集成到c++中真的有意义吗?

As every other answerer has already pointed out, C++ provides lots of heap-allocation mechanisms (std::unique_ptr<int[]> A = new int[n]; or std::vector<int> A(n); being the obvious ones) when you really want to convey the idea "I have no idea how much RAM I might need." And C++ provides a nifty exception-handling model for dealing with the inevitable situation that the amount of RAM you need is greater than the amount of RAM you have. But hopefully this answer gives you a good idea of why C99-style VLAs were not a good fit for C++ — and not really even a good fit for C99. ;)


有关该主题的更多信息,请参阅Bjarne Stroustrup 2013年10月关于VLAs的论文N3810“阵列扩展的替代方案”。Bjarne的POV与我的非常不同;N3810更侧重于为这些东西找到一个好的c++语法,并反对在c++中使用原始数组,而我更关注元编程和类型系统的含义。我不知道他是否认为元编程/类型系统的含义是已解决的、可解决的,还是仅仅是无趣的。


“合理使用可变长度数组”(Chris Wellons, 2019-10-27)是一篇很好的博客文章,触及了许多相同的观点。

这样的数组是C99的一部分,但不是标准c++的一部分。正如其他人所说,向量总是一个更好的解决方案,这可能就是为什么可变大小数组不在c++标准中(或在提议的c++ 0x标准中)。

顺便说一句,关于“为什么”c++标准是这样的问题,可以去Usenet新闻组comp. stdc++。

为此使用std::vector。例如:

std::vector<int> values;
values.resize(n);

内存将在堆上分配,但这只会带来很小的性能缺陷。此外,不要在堆栈上分配大数据锁是明智的,因为它的大小相当有限。

似乎c++ 14中也可以使用:

https://en.wikipedia.org/wiki/C%2B%2B14#Runtime-sized_one_dimensional_arrays

更新:它没有进入c++ 14。

In my own work, I've realized that every time I've wanted something like variable-length automatic arrays or alloca(), I didn't really care that the memory was physically located on the cpu stack, just that it came from some stack allocator that didn't incur slow trips to the general heap. So I have a per-thread object that owns some memory from which it can push/pop variable sized buffers. On some platforms I allow this to grow via mmu. Other platforms have a fixed size (usually accompanied by a fixed size cpu stack as well because no mmu). One platform I work with (a handheld game console) has precious little cpu stack anyway because it resides in scarce, fast memory.

我并不是说永远不需要将可变大小的缓冲区推入cpu堆栈。说实话,当我发现这不是标准的时候,我很惊讶,因为这个概念似乎很适合语言。但对我来说,“可变大小”和“必须物理地位于cpu堆栈上”这两个需求从来没有同时出现过。这与速度有关,所以我做了自己的“数据缓冲区并行堆栈”。