【问题标题】:C++ array size dependent on function parameter causes compile errorsC++ 数组大小取决于函数参数会导致编译错误
【发布时间】:2010-09-23 15:36:39
【问题描述】:

我有一个简单的函数,其中声明一个数组的大小 取决于 int 的参数。

    void f(int n){
        char a[n];
    };

    int main() {
        return 0;
    }

这段代码在 GNU C++ 上编译得很好,但在 MSVC 2005 上编译得不好。

我收到以下编译错误:

    .\main.cpp(4) : error C2057: expected constant expression
    .\main.cpp(4) : error C2466: cannot allocate an array of constant size 0
    .\main.cpp(4) : error C2133: 'a' : unknown size

我能做些什么来纠正这个问题?

(我有兴趣使用 MSVC 进行这项工作,而不使用 new/delete)

【问题讨论】:

    标签: c++ arrays parameters declaration


    【解决方案1】:

    您发现它是 Gnu 编译器对 C++ 语言的扩展之一。在这种情况下,Visual C++ 是完全正确的。 C++ 中的数组必须使用编译时常量表达式的大小来定义。

    在 1999 年对 C 语言的更新中添加了一个特性,称为可变长度数组,这是合法的。如果你能找到一个支持C99的C编译器,这并不容易。但是这个特性不是标准 C++ 的一部分,也不会在 C++ 标准的下一次更新中添加。

    C++ 中有两种解决方案。第一个是使用std::vector,第二个只是使用运算符new []:

    char *a = new char [n];
    

    在我写答案时,另一个人发布了使用 _alloca 的建议。我强烈建议不要这样做。您只需将一种非标准、不可移植的方法换成另一种编译器特定的方法。

    【讨论】:

    • 是的,但是“new”所做的从堆分配与从堆栈分配(OP 试图做的)有很大不同。 (这可能是他试图编译的性能敏感代码。)
    • 暂时不用太担心性能,我认为工作很自然......但如果它不是 C++ 标准的一部分,那么我理解
    • Re: _alloca: OP 只询问了如何让等效代码在 MSVC 上工作并且不使用 new/delete。
    • the second is just to use 此处使用“just”一词意味着手动管理动态数组生存期比拥有std::vector 对象要简单。这远非事实。
    【解决方案2】:

    您从堆栈分配的方法是 g++ 扩展。要在 MSVC 下做等价,你需要使用 _alloca:

    char *a = (char *)_alloca(n);
    

    【讨论】:

    • 哦,所以它在堆栈上分配!太棒了:)谢谢!
    • 请注意 alloca 手册页中的这条评论:BUGS alloca 函数依赖于机器和编译器。在许多系统上,它的实现是错误的。不鼓励使用它。
    • 是的,但它在 MSVC 下肯定可以工作,这就是 OP 试图让他的代码工作的原因。我自己多年来一直在使用它。
    • 为什么aloca 不被认为是好的做法? stackoverflow.com/questions/1018853/…
    【解决方案3】:

    您正在使用非标准的东西。实际上它是标准 C 但不是 C++。这是多么奇特啊!

    再解释一下,运行时大小的堆栈数组不是 C++ 的一部分,而是 C99 的一部分,这是 C 的最新标准。这就是为什么有些编译器会得到它,而其他编译器不会。我建议不要使用它,以避免编译器兼容性问题。

    该功能的替代实现将使用 new 和 delete,正如 strager 所发布的。

    【讨论】:

    • 一点都不“特别”..!
    【解决方案4】:

    您可以使用 new/delete 在堆上分配/释放内存。这比使用 char[n] 更慢并且可能更容易出错,但遗憾的是,它还不是 C++ 标准的一部分。

    您可以使用 boost 的作用域数组类作为使用 new[] 的异常安全方法。 a 超出范围时会自动调用 delete[]。

    void f(int n) {
        boost::scoped_array<char> a(new char[n]);
    
        /* Code here. */
    }
    

    你也可以使用std::vector和reserve()一些字节:

    void f(int n) {
        std::vector<char> a;
        a.resize(n);
    
        /* Code here. */
    }
    

    如果您确实想使用 char[n],请编译为 C99 代码而不是 C++ 代码。

    如果出于某种原因绝对必须在堆栈上分配数据,请使用 _alloca 或 _malloca/_freea,它们是 MSVC 库等提供的扩展。

    【讨论】:

    • 是的,但我不明白为什么 g++ 在 MSVC 失败时对此没有问题
    • 这是错误的,因为它是从堆中分配的。他想在堆栈上分配,这就是 g++ 版本所做的。 MSVC不编译原版的原因是它是g++扩展。
    • 他不能用 MSVC 把它放在堆栈上。他可以把它放在堆上,或者让它大小恒定,没有办法用 MSVC 在堆栈上分配一个可变大小的数组。
    • 是的,你可以。看我的回答。 (提示:_alloca :))
    • @Jim Buck _alloca 与使用 new/delete 相同,在我发布之前我就知道这一点。 @strager 我已经指定我确信我从一开始就在编译 C++ 代码(见这篇文章的标题)。
    【解决方案5】:

    可变长度数组是在 C99 中引入的。它在 gcc 中受支持,但在 msvc 中不受支持。据 MSVC 团队的一位人士称,微软没有计划在他们的 c/C++ 编译器中支持此功能。他建议在这些情况下使用 std::vector。

    请注意,C99 并不要求在堆栈上分配数组。编译器可以在堆上分配它。但是,gcc 确实在堆栈上分配了数组。

    【讨论】:

      【解决方案6】:

      通常在 C(除了其他人指出的 C99 编译器)和 C++ 中,如果要在堆栈上分配内存,则必须在编译时知道要分配的大小。局部变量是在堆栈上分配的,因此在运行时长度取决于函数参数的数组违反了此规则。 Klein 正确地指出使用“new”运算符是解决此问题的一种方法:

      char *a = new char [n];

      'a' 仍然是分配在堆栈上的局部变量,但不是整个数组(具有可变长度),它只是一个指向数组的指针(它总是相同的大小,因此在编译时已知时间)。数组是在堆上分配的,它通常播放堆栈的对应物——堆栈用于在编译时知道大小的东西,而堆用于在编译时不知道大小的东西。

      【讨论】:

        【解决方案7】:

        使用vector&lt;&gt; 而不是数组是否合理?或者,因为您要替换 char *std::string?这些确实适用于运行时大小调整,尽管可能有其他原因不使用它们。

        【讨论】:

          猜你喜欢
          • 2019-07-21
          • 2015-08-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-19
          • 1970-01-01
          相关资源
          最近更新 更多