在过去的几年中,我没有很大程度使用 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
}

vector版本变得相当不太妥当

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

也可能将所有通过内存传播切片、 行和列。

它看在comp.std.c++的讨论是明确这个问题是非常有争议与一些非常重量级名称参数的两侧。它并不是明显std::vector始终是更好的解决方案。

2009-12-11 10:15:40
问题评论:

只是出于好奇心,为何它需要在堆栈上分配?是,恐怕堆分配性能问题?

@Dimitri 不确实,但无可置疑,堆栈分配将会更快堆分配比。并且,在某些情况下这可能会产生影响。

所有数据都都紧靠在一起以便当您循环访问该数组读取和写入彼此相邻的字节的可变长度数组的主要优点。到高速缓存中获取数据和 cpu 可以处理它而不会读取和发送到从内存的字节数。

数组是可变长度也可能用于预处理器常量替换静态常数变量。也在 C 中不具有对 VLA,另一个选项,它有时需要编写可移植的 C/c + + 代码 (使用这两种编译器兼容)。

此外,似乎 clang + + 中允许 VLAs。

回答:

有最近已就此开始时间在 usenet 展开讨论︰为什么在 C + + 0x 中没有 VLAs.

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

C99 VLAs 可以提供能够而不会浪费空间或未使用的元素,调用构造函数创建小数组的小优势,但他们将类型系统引起相当大的更改 (需要能够指定根据运行时的值的类型-这尚不存在当前 c + + 中除new运算符类型说明符,但要进行专门处理以便运行库等没有转义的new运算符的范围)。

您可以使用std::vector,但不完全相同,因为它使用的动态内存中,并使其使用自己的堆栈分配并不完全很容易 (对齐方式是个问题,太)。因为一个向量是一个可调整大小的容器中,而 VLAs 是固定大小,它还不能解决同样的问题。C + + 动态数组方案旨在引入一种基于库解决方案,为语言的替代基于 VLA。但是,它不打算参加 C + + 0x,据我所知。

+ 1 和接受。一个注释也是如此,我认为安全参数是有点弱,因为有这么多其他方法导致堆栈溢出。安全参数可用于支持不应再使用递归,则从堆中分配的所有对象的位置。

因此,你所说,因为有其他的方法导致堆栈溢出,我们可能也鼓励多种排列?

@Andreas,同意有关的弱点。但对于递归过程,需要大量的调用直到堆栈被吃掉,并且如果这可能是这种情况,人们会使用迭代。在 usenet 线程上的有些人说,不过,这不是在所有情况下对 VLAs 参数,因为有时要明确知道上限。但在这些情况下,从什么我见一个静态数组可以同样就足够了,因为它不会浪费空间吗 (如果它然后您将实际上不得不问堆栈区域是否足够大,再次)。

此外看 Matt Austern 在该线程中的答案︰ 语言规范的 VLAs 将复杂可能得多 c + +,由于更严格符合 c + + 中的类型 (示例︰ C 允许分配T(*)[] T(*)[N] -c + + 中这不允许的因为 c + + 并不了解"类型兼容性"-它需要完全匹配),键入参数、 例外、 骗子和析构函数和 stuffs。我不敢肯定是否 VLAs 的优势会得到丰厚的所有工作。但是,然后,我以前从未在现实生活中,使用 VLAs,因此我可能不知道正常使用情况下它们。

@AHelps︰ 什么是最好的或许是行为有点类似于vector但需要固定的后进先出的使用模式,并保持一个或更多线程静态分配的缓冲区分配总额最大线程以往任何时候都已用完,按照一般大小的一种但它无法显式进行修整。普通的"分配"通常情况下需要只是复制指针、 指针的指针相减、 整数比较和指针加法;取消分配将只需要指针副本。不比 VLA 要慢得多。

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

在 C99 中的可变长度数组基本上是 misstep。为了支持 VLAs,C99 不得不进行常识到下列优惠︰

  • sizeof x不再始终是一个编译时常量;有时,编译器必须生成代码,以便计算sizeof-在运行时的表达式。

  • 允许二维 VLAs (int A[x][y]) 所需的新语法声明函数接受作为参数的二维 VLAs: void foo(int n, int A[][*]).

  • 不太重要的是在 c + + 环境下,但 C 的目标受众的嵌入式系统程序员非常重要声明 VLA 意味着 chomping 的堆栈的任意大块。这是保证堆栈溢出和崩溃。(声明int A[n],只要您隐式地断言有 2 GB 空闲堆栈。毕竟,如果您知道n是肯定小于 1000 此处",然后将只需声明int A[1000]替换为1000的 32 位整数n是许可,有不知道什么您程序的行为必须具备的条件。

好了,让我们转向现在谈论 c + +。在 c + +,我们有相同强区分"类型系统"和"价值系统",C89...但我们确实已开始依赖 C 所不具有的方式。例如︰

template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s;  // equivalently, S<int[n]> s;

如果n不是一个编译时常数 (即,如果A的不定修改的类型),然后在地球是什么类型的SS的类型仅在运行时确定吗?

有关此内容︰

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 样式 VLAs 集成到 c + +?

每个其他答复者已经指出,c + + 提供了大量的堆分配机制 (std::unique_ptr<int[]> A = new int[n];std::vector<int> A(n);正在明显的) 当您确实想要传达一下"我不知道我可能需要的 RAM 的数量。"和 c + + 提供了极好的异常处理模型处理不可避免的情况所需的 RAM 的数量大于您的 RAM 的数量。但愿答案为您提供了一个好主意,但为什么不 C99 样式 VLAs 的良好符合 c + + 的 — — 甚至实际上不是非常适合的 C99。;)


有关该主题的详细信息,请参阅N3810"的阵列扩展的备选方案",Bjarne Stroustrup 2013 年 10 月在 VLAs 上的纸。Bjarne 的 POV 是非常不同于我;N3810 主要多和 discouraging 使用的 c + + 中的原始阵列上查找 ish 好 C + +语法的事情,而我更关注的含义元编程和 typesystem。我不知道是否他认为的元编程/typesystem 问题解决,解决的或只是攻击者的吸引力。

我同意 VLAs 就错了。更广泛应用,并更加有用, alloca()应具有已标准化在 C99 相反。VLAs 是标准委员会跳转出提前实施,而不是在其他方向会发生什么情况。

我没有意识到允许作为函数参数的 VLAs,C 安排。我同意 (什么是值得,因为我不写编译器) 而是可怕的。作为本地变量 VLAs 另一方面似乎确实合理的给我。可以溢出堆栈中,但您必须考虑一下,任何时候您使用局部变量,因此这有没有什么真正的新授权。还有"sizeof"事-不,只是一种功能的价格吗?

不定修改类型系统的极好补充 IMO,且无项目符号点违反常识。(1) C 标准不区分"编译时间"和"运行时间"因此,这是不成问题的问题;(2) *是可选的您可以 (并且应该) 编写int A[][n];(3) 可以使用类型系统而无需实际声明任何 VLAs。例如一个函数可以接受的不定修改的类型,数组和调用可与非 VLA 的二维数组的不同维度。但是您在您的帖子的后半部分中进行有效点。

在过渡到 gsl::span <>,类型参数变得尤为明显。VLA (因为 g + + 支持那些作为扩展的 c + +) 没有意义时正在通过模板试图推断出它的大小。

您知道,所有那些您所述的有用东西会继续存在即使支持 VLAs-它将不编译为 VLAs,只为固定长度数组。不是我不同意您 VLA 保留,只是它们是有关使用比有关功能的详细信息。做的reinterpret_casts void *函数指针也意味着意外的行为和可能的堆栈溢出,但这并不意味着它不应该有可能。

始终可以使用 alloca() 来分配内存堆栈上运行时,如果您希望︰

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

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

小提示︰ 如所提到的在 alloca(3) 的 Mac OS X 手册页,"alloca() 函数是机器和编译器相关;它的使用是疏散 couraged。只要您知道。

此外,alloca() 的作用域是代码的整个函数,而不只是代码的包含该变量块。因此使用它在一个循环内将不断增大堆栈。VLA 就没有这个问题。

这被认为是包含在 C + + /cli 1 x,但删除了(这是对我先前说的更正)。

会不太适用于 c + + 仍然因为我们已经有std::vector填充此角色。

不,我们并非,std::vector 不分配堆栈上的数据。:)

"堆栈"的实现细节;只要满足有关对象生存期的保证时,编译器可能会从任何地方分配内存。

@M.M︰ 公平足够多,但在练习中我们仍不能使用std::vector而不是,说, alloca().

您可以根据您的程序中获取正确的输出 @einpoklum。性能是质量的实现问题

在其中分配堆内存是非常昂贵的情况下执行的操作进行比较。例如,矩阵数学。如果您使用 smallish 矩阵说 5 到 10 个元素并进行大量的 arithmetics malloc 开销将会真的很大。在同一时间进行编译的大小时常似乎非常浪费且不灵活。

我认为 c + + 是不安全本身,"尝试以添加更多不安全功能"的参数不是非常强。另一方面,根据 c + + 可以说是最高效的运行从而使更多的编程语言功能因此始终是有用︰ 人写关键程序的性能在很大程度将 c + +,并且他们需要尽可能多地性能。从堆进行堆栈移动的东西是这样一种可能性。减少的堆块是另一种。使 VLAs 对象成员一样实现此目标的一种方法。我正在研究这些建议。当然,它是有点复杂,若要实现,但它看起来很可行。

内容来源于Stack Overflow Variable-length arrays in C++?
请输入您的翻译

Variable-length arrays in C++?

确认取消