在过去的几年里,我不怎么使用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并不总是更好的解决方案。


当前回答

如果你愿意,你总是可以在运行时使用alloca()在堆栈上分配内存:

void foo (int n)
{
    int *values = (int *)alloca(sizeof(int) * n);
}

在堆栈上分配意味着当堆栈展开时它将自动被释放。

注意:正如在Mac OS X的alloca(3)手册中提到的,“alloca()函数依赖于机器和编译器;不鼓励使用它。”告诉你一声。

其他回答

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堆栈上”这两个需求从来没有同时出现过。这与速度有关,所以我做了自己的“数据缓冲区并行堆栈”。

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

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

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

VLAs是可变修改类型家族的一部分。 这类类型非常特殊,因为它们有运行时组件。

代码:

int A[n];

被编译器视为:

typedef int T[n];
T A;

注意,数组的运行时大小并不与变量A绑定,而是与变量的类型绑定。

没有什么可以阻止创建这种类型的新变量:

T B,C,D;

或者指针或数组

T *p, Z[10];

此外,指针允许创建动态存储的vla。

T *p = malloc(sizeof(T));
...
free(p);

这消除了一个流行的神话,即VLAs只能在堆栈上分配。

回到刚才的问题。

这个运行时组件不能很好地与类型演绎一起工作,而类型演绎是c++类型系统的基础之一。它不可能使用模板,演绎和重载。

c++类型系统是静态的,所有类型都必须在编译时完全定义或推导。 虚拟机类型只在程序执行时完成。 在已经非常复杂的c++中引入虚拟机类型的额外复杂性被认为是不合理的。主要是因为它们主要的实际应用 是自动vla (int A[n];),它们有std::vector的替代形式。

这有点令人遗憾,因为VM类型为处理多维数组的程序提供了非常优雅和高效的解决方案。

在C语言中,你可以简单地写:

void foo(int n, int A[n][n][n]) {
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
      for (int k = 0; k < n; ++k)
        A[i][j][k] = i * j * k;
}

...

int A[5][5][5], B[10][10][10];
foo(5, A);
foo(10, B);

现在尝试在c++中提供高效和优雅的解决方案。

如果你愿意,你总是可以在运行时使用alloca()在堆栈上分配内存:

void foo (int n)
{
    int *values = (int *)alloca(sizeof(int) * n);
}

在堆栈上分配意味着当堆栈展开时它将自动被释放。

注意:正如在Mac OS X的alloca(3)手册中提到的,“alloca()函数依赖于机器和编译器;不鼓励使用它。”告诉你一声。

在某些情况下,与所执行的操作相比,分配堆内存的开销非常大。矩阵数学就是一个例子。如果你处理较小的矩阵,比如5到10个元素,并做大量的算术运算,malloc开销将非常大。同时,将大小设置为编译时常量似乎非常浪费且不灵活。

I think that C++ is so unsafe in itself that the argument to "try to not add more unsafe features" is not very strong. On the other hand, as C++ is arguably the most runtime efficient programming language features which makes it more so are always useful: People who write performance critical programs will to a large extent use C++, and they need as much performance as possible. Moving stuff from heap to stack is one such possibility. Reducing the number of heap blocks is another. Allowing VLAs as object members would one way to achieve this. I'm working on such a suggestion. It is a bit complicated to implement, admittedly, but it seems quite doable.