长度数组是C ++标准的一部分?

在过去的几年中我并没有使用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永远不是一个更好的解决方案。


    最近有一个关于usenet启动的讨论:为什么没有C ++ 0x中的VLA。

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

    C99 VLA可以提供创建小型数组的小优点,而不会浪费空间或为未使用的元素调用构造函数,但是它们会对类型系统引入相当大的变化(您需要能够根据运行时值指定类型 - this在当前的C ++中还不存在,除了new运算符类型说明符,但是它们被专门处理,以便运行时性能不会脱离new运算符的范围)。

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


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

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

  • sizeof x不再总是编译时常量; 编译器有时必须生成代码以在运行时评估sizeof expression。

  • 允许二维的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位整数n代替1000表示你不知道你的程序的行为应该是什么。)

  • 好的,现在让我们谈谈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 ++中是否有意义?

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


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


    如果您希望:在运行时您总是可以使用alloca()在堆栈上分配内存。

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

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

    快速提示:正如在Mac OS X手册页中提到的alloca(3)所述,“alloca()函数依赖于机器和编译器,它的使用是不受欢迎的。” 就这样你知道。

    链接地址: http://www.djcxy.com/p/18573.html

    上一篇: length arrays part of the C++ standard?

    下一篇: LeftNavBar creates black bar at the top of the activity