为什么可变长度数组不是 C++ 标准的一部分?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1887097/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:29:29  来源:igfitidea点击:

Why aren't variable-length arrays part of the C++ standard?

c++arraysstandardsvariable-length-arrayvariable-length

提问by Andreas Brinck

I haven't used C very much in the last few years. When I read this questiontoday I came across some C syntax which I wasn't familiar with.

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

Apparently in C99the following syntax is valid:

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

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

This seems like a pretty useful feature. Was there ever a discussion about adding it to the C++ standard, and if so, why it was omitted?

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

Some potential reasons:

一些潜在的原因:

  • Hairy for compiler vendors to implement
  • Incompatible with some other part of the standard
  • Functionality can be emulated with other C++ constructs
  • 编译器供应商要实现的毛茸茸的
  • 与标准的其他部分不兼容
  • 可以使用其他 C++ 构造来模拟功能

The C++ standard states that array size must be a constant expression (8.3.4.1).

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

Yes, of course I realize that in the toy example one could use std::vector<int> values(m);, but this allocates memory from the heap and not the stack. And if I want a multidimensional array like:

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

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

the vectorversion becomes pretty clumsy:

vector版本变得很笨拙:

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

The slices, rows and columns will also potentially be spread all over memory.

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

Looking at the discussion at comp.std.c++it's clear that this question is pretty controversial with some very heavyweight names on both sides of the argument. It's certainly not obvious that a std::vectoris always a better solution.

看一下讨论,comp.std.c++很明显这个问题很有争议,争论双方都有一些非常重量级的名字。astd::vector总是更好的解决方案当然并不明显。

采纳答案by Johannes Schaub - litb

There recently was a discussion about this kicked off in usenet: Why no VLAs in C++0x.

最近在 usenet 上有一个关于这个的讨论:Why no VLAs in C++0x

I agree with those people that seem to agree that having to create a potential large array on the stack, which usually has only little space available, isn't good. The argument is, if you know the size beforehand, you can use a static array. And if you don't know the size beforehand, you will write unsafe code.

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

C99 VLAs could provide a small benefit of being able to create small arrays without wasting space or calling constructors for unused elements, but they will introduce rather large changes to the type system (you need to be able to specify types depending on runtime values - this does not yet exist in current C++, except for newoperator type-specifiers, but they are treated specially, so that the runtime-ness doesn't escape the scope of the newoperator).

C99 VLA 可以提供一个小的好处,即能够在不浪费空间或为未使用的元素调用构造函数的情况下创建小数组,但它们会给类型系统带来相当大的变化(您需要能够根据运行时值指定类型 - 这在当前的 C++ 中尚不存在,除了new运算符类型说明符,但它们被特殊对待,因此运行时性不会超出new运算符的范围)。

You can use std::vector, but it is not quite the same, as it uses dynamic memory, and making it use one's own stack-allocator isn't exactly easy (alignment is an issue, too). It also doesn't solve the same problem, because a vector is a resizable container, whereas VLAs are fixed-size. The C++ Dynamic Arrayproposal is intended to introduce a library based solution, as alternative to a language based VLA. However, it's not going to be part of C++0x, as far as I know.

您可以使用std::vector,但它并不完全相同,因为它使用动态内存,并且使其使用自己的堆栈分配器并不容易(对齐也是一个问题)。它也没有解决同样的问题,因为向量是一个可调整大小的容器,而 VLA 是固定大小的。在C ++动态阵列提议旨在介绍文库的基于溶液,作为替代基于VLA的语言。但是,据我所知,它不会成为 C++0x 的一部分。

回答by Quuxplusone

(Background: I have some experience implementing C and C++ compilers.)

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

Variable-length arrays in C99 were basically a misstep. In order to support VLAs, C99 had to make the following concessions to common sense:

C99 中的可变长度数组基本上是一个失误。为了支持 VLA,C99 不得不对常识做出以下让步:

  • sizeof xis 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 largechunk of your stack. This is a guaranteedstack-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 "nis definitely less than 1000 here", then you would just declare int A[1000]. Substituting the 32-bit integer nfor 1000is an admission that you have no idea what the behavior of your program ought to be.)

  • 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 位整数替换n1000是一种承认你不知道你的程序的行为应该是什么。)

Okay, so let's move to talking about C++ now. In C++, we have the same strong distinction between "type system" and "value system" that C89 does… but we've really started to rely on it in ways that C has not. For example:

好的,让我们现在开始讨论 C++。在 C++ 中,我们在“类型系统”和“值系统”之间有着与 C89 相同的强烈区分……但我们真的开始以 C 没有的方式依赖它。例如:

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

If nweren't a compile-time constant (i.e., if Awere of variably modified type), then what on earth would be the type of S? Would S's type alsobe determined only at runtime?

如果n不是编译时常量(即,如果A是可变修改的类型),那么到底是什么类型的S?会S的类型只能在运行时确定?

What about this:

那这个呢:

template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);

The compiler must generate code for some instantiation of myfunc. What should that code look like? How can we statically generate that code, if we don't know the type of A1at compile time?

编译器必须为myfunc. 这段代码应该是什么样的?如果我们A1在编译时不知道类型,我们如何静态生成该代码?

Worse, what if it turns out at runtime that n1 != n2, so that !std::is_same<decltype(A1), decltype(A2)>()? In that case, the call to myfuncshouldn't even compile, because template type deduction should fail! How could we possibly emulate that behavior at runtime?

更糟糕的是,如果在运行时结果是n1 != n2,那么!std::is_same<decltype(A1), decltype(A2)>()呢?在这种情况下,myfunc甚至不应该调用compile,因为模板类型推导应该失败!我们怎么可能在运行时模拟这种行为?

Basically, C++ is moving in the direction of pushing more and more decisions into compile-time: template code generation, constexprfunction evaluation, and so on. Meanwhile, C99 was busy pushing traditionally compile-timedecisions (e.g. sizeof) into the runtime. With this in mind, does it really even make sense to expend any effort tryingto integrate C99-style VLAs into C++?

基本上,C++ 正朝着将越来越多的决策推入编译期的方向发展:模板代码生成、constexpr函数评估等。同时,C99 正忙于将传统的编译时决策(例如sizeof)推入运行时。考虑到这一点,它真的甚至是有意义的花费任何力气试图以C99风格VLAS整合到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 thisanswer gives you a good idea of why C99-style VLAs were nota good fit for C++ — and not really even a good fit for C99. ;)

正如其他所有回答者已经指出的那样,当您真的想传达“我不知道我可能需要多少 RAM”的想法时,C++ 提供了许多堆分配机制(std::unique_ptr<int[]> A = new int[n];std::vector<int> A(n);显而易见的机制)。C++ 提供了一个漂亮的异常处理模型来处理不可避免的情况,即您需要的 RAM 量大于您拥有的 RAM 量。但希望这个答案能让您很好地了解为什么 C99 风格的 VLA适合 C++——甚至不适合 C99。;)



For more on the topic, see N3810 "Alternatives for Array Extensions", Bjarne Stroustrup's October 2013 paper on VLAs. Bjarne's POV is very different from mine; N3810 focuses more on finding a good C++ish syntaxfor the things, and on discouraging the use of raw arrays in C++, whereas I focused more on the implications for metaprogramming and the typesystem. I don't know if he considers the metaprogramming/typesystem implications solved, solvable, or merely uninteresting.

有关该主题的更多信息,请参阅N3810“阵列扩展的替代方案”,Bjarne Stroustrup 2013 年 10 月关于 VLA 的论文。Bjarne 的 POV 与我的非常不同;N3810 更侧重于为事物寻找良好的 C++ish语法,并且不鼓励在 C++ 中使用原始数组,而我更侧重于对元编程和类型系统的影响。我不知道他是否认为元编程/类型系统的含义已解决、可解决,或者仅仅是无趣。



A good blog post that hits many of these same points is "Legitimate Use of Variable Length Arrays"(Chris Wellons, 2019-10-27).

一篇很好的博文是“可变长度数组的合法使用”(Chris Wellons,2019-10-27)。

回答by PfhorSlayer

You could always use alloca() to allocate memory on the stack at runtime, if you wished:

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

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

Being allocated on the stack implies that it will automatically be freed when the stack unwinds.

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

Quick note: As mentioned in the Mac OS X man page for alloca(3), "The alloca() function is machine and compiler dependent; its use is dis-couraged." Just so you know.

快速说明:如 alloca(3) 的 Mac OS X 手册页所述,“alloca() 函数依赖于机器和编译器;不鼓励使用它。” 只要你知道。

回答by Eric

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.

在我自己的工作中,我意识到每次我想要变长自动数组或 alloca() 之类的东西时,我并不真正关心内存是否物理位于 cpu 堆栈上,只是它来自一些堆栈分配器不会导致对一般堆的缓慢旅行。所以我有一个每线程对象,它拥有一些内存,它可以从中推送/弹出可变大小的缓冲区。在某些平台上,我允许它通过 mmu 增长。其他平台有固定大小(通常也伴随有固定大小的 cpu 堆栈,因为没有 mmu)。我使用的一个平台(掌上游戏机)无论如何都有宝贵的很少的 cpu 堆栈,因为它驻留在稀缺、快速的内存中。

I'm not saying that pushing variable-sized buffers onto the cpu stack is never needed. Honestly I was surprised back when I discovered this wasn't standard, as it certainly seems like the concept fits into the language well enough. For me though, the requirements "variable size" and "must be physically located on the cpu stack" have never come up together. It's been about speed, so I made my own sort of "parallel stack for data buffers".

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

回答by Viktor Sehr

Seems it will be available in C++14:

似乎它将在 C++14 中可用:

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

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

Update: It did not make it into C++14.

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

回答by Bengt Gustafsson

There are situations where allocating heap memory is very expensive compared to the operations performed. An example is matrix math. If you work with smallish matrices say 5 to 10 elements and do a lot of arithmetics the malloc overhead will be really significant. At the same time making the size a compile time constant does seem very wasteful and inflexible.

在某些情况下,与执行的操作相比,分配堆内存的成本非常高。一个例子是矩阵数学。如果您使用较小的矩阵(例如 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++ 本身是如此不安全,以至于“尽量不添加更多不安全功能”的论点并不是很强烈。另一方面,由于 C++ 可以说是运行时效率最高的编程语言特性,因此它总是更有用:编写性能关键程序的人将在很大程度上使用 C++,他们需要尽可能多的性能。将东西从堆移动到堆栈就是这样一种可能性。减少堆块的数量是另一回事。允许 VLA 作为对象成员是实现这一目标的一种方式。我正在研究这样的建议。诚然,实现起来有点复杂,但似乎很可行。

回答by philsquared

This was considered for inclusion in C++/1x, but was dropped(this is a correction to what I said earlier).

这被考虑包含在 C++/1x 中,但被删除了(这是对我之前所说的更正)。

It would be less useful in C++ anyway since we already have std::vectorto fill this role.

无论如何,它在 C++ 中的用处不大,因为我们已经必须std::vector填补这个角色。

回答by Dimitri C.

Use std::vector for this. For example:

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

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

The memory will be allocated on the heap, but this holds only a small performance drawback. Furthermore, it is wise not to allocate large datablocks on the stack, as it is rather limited in size.

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

回答by Jingguo Yao

C99 allows VLA. And it puts some restrictions on how to declare VLA. For details, refer to 6.7.5.2 of the standard. C++ disallows VLA. But g++ allows it.

C99 允许 VLA。它对如何声明 VLA 施加了一些限制。详见标准6.7.5.2。C++ 不允许 VLA。但是 g++ 允许它。

回答by Jingguo Yao

Arrays like this are part of C99, but not part of standard C++. as others have said, a vector is always a much better solution, which is probably why variable sized arrays are not in the C++ standatrd (or in the proposed C++0x standard).

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

BTW, for questions on "why" the C++ standard is the way it is, the moderated Usenet newsgroup comp.std.c++is the place to go to.

顺便说一句,对于“为什么”C++ 标准是这样的问题,请访问受审核的 Usenet 新闻组comp.std.c++