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


当前回答

最近在usenet上有一个关于这个问题的讨论:为什么c++ 0x中没有VLAs。

我同意那些似乎同意必须在堆栈上创建一个潜在的大数组的人,通常只有很少的可用空间,这是不好的。参数是,如果你事先知道大小,你可以使用静态数组。如果事先不知道大小,就会编写不安全的代码。

C99 VLAs可以在不浪费空间或调用未使用元素的构造函数的情况下创建小型数组,但它们将对类型系统带来相当大的更改(您需要能够根据运行时值指定类型-目前c++中还不存在这种功能,除了新的操作符类型说明符,但它们经过了特殊处理,因此运行时性不会逃脱新操作符的作用域)。

你可以使用std::vector,但它并不完全相同,因为它使用动态内存,并且让它使用自己的堆栈分配器并不完全容易(对齐也是一个问题)。它也不能解决同样的问题,因为vector是一个可调整大小的容器,而VLAs是固定大小的。c++动态数组提案旨在引入一个基于库的解决方案,作为基于语言的VLA的替代方案。然而,据我所知,它不会成为c++ 0x的一部分。

其他回答

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

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

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

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

最近在usenet上有一个关于这个问题的讨论:为什么c++ 0x中没有VLAs。

我同意那些似乎同意必须在堆栈上创建一个潜在的大数组的人,通常只有很少的可用空间,这是不好的。参数是,如果你事先知道大小,你可以使用静态数组。如果事先不知道大小,就会编写不安全的代码。

C99 VLAs可以在不浪费空间或调用未使用元素的构造函数的情况下创建小型数组,但它们将对类型系统带来相当大的更改(您需要能够根据运行时值指定类型-目前c++中还不存在这种功能,除了新的操作符类型说明符,但它们经过了特殊处理,因此运行时性不会逃脱新操作符的作用域)。

你可以使用std::vector,但它并不完全相同,因为它使用动态内存,并且让它使用自己的堆栈分配器并不完全容易(对齐也是一个问题)。它也不能解决同样的问题,因为vector是一个可调整大小的容器,而VLAs是固定大小的。c++动态数组提案旨在引入一个基于库的解决方案,作为基于语言的VLA的替代方案。然而,据我所知,它不会成为c++ 0x的一部分。

在某些情况下,与所执行的操作相比,分配堆内存的开销非常大。矩阵数学就是一个例子。如果你处理较小的矩阵,比如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.

这是考虑包含在c++ /1x中,但被放弃了(这是对我前面所说的更正)。

因为我们已经有了std::vector来填充这个角色,所以它在c++中就没有那么有用了。

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++中提供高效和优雅的解决方案。