运行时允许没有动态分配的数组大小吗?

83

我使用C++已经有几年了,今天我看到一些代码,但是这怎么可能是合法的?

int main(int argc, char **argv)
{
    size_t size;
    cin >> size;
    int array[size];
    for(size_t i = 0; i < size; i++)
    {
        array[i] = i;
        cout << i << endl;
    }

    return 0;
}

使用GCC编译。

如何在运行时确定大小而不使用newmalloc

只是为了双重检查,我已经谷歌了一些,所有类似于我的代码的代码都声称会给出存储大小错误。

甚至Deitel的《C++ How To Program》p.261在常见编程错误4.5下陈述:

只有常量可用于声明自动和静态数组的大小。

启发我。


3
请注意,DMA表示“直接内存访问” - 我认为您正在询问动态分配。 - anon
3
C还是C++?选_一个_。 - Lightness Races in Orbit
1
再次选择一个:C或C++。这个问题明确且仅涉及C;不应该有C++标签。 - anon
8个回答

75

这在C99中是有效的。

C99标准支持在栈上使用可变大小的数组。可能您的编译器也选择支持这个结构。

请注意,这与mallocnew是不同的。gcc在栈上分配数组,就像对int array[100]进行调整堆栈指针一样。没有进行堆分配。这很类似于_alloca


14
我在我们代码库中的一个文件中发现了相同的情况,而那个文件是几个月前编写的。我感到很困惑,我们团队的其他成员也是如此,因为不知道为什么它可以编译通过。在我们的情况下,数组的大小在声明数组之前就被计算出来了(这也不应该被允许,是吗?)总之,一个挑战被提出来了。任何能回答为什么这是合法的人都会得到一块波普塔特饼干。如果你曾经来到西雅图,请告诉我,我会给你一块波普塔特饼干。 - Jeff Lamb
2
你能提供一些关于堆栈在这种情况下内部工作的信息/链接吗?这会在运行时引入一些额外开销吗? - balki
2
@balki 开销很小,因为它基本上是增加/减少堆栈指针。如果在函数开始处保存原始堆栈指针,则堆栈行为可以与正常情况基本相同。 - Mehrdad Afshari
在C++中允许这样吗?Visual Studio不允许这样吗? - justpraveen
我不明白的是,当汇编器生成汇编代码时,它并不知道数组的确切大小。那么它要移动堆栈指针多少偏移量来为这个动态大小的数组腾出空间呢?它无法确定。因此,了解这是如何实现的非常有趣。 - torez233

27

这被称为变长数组(VLAs)。它在C99中是标准的,但GCC允许将其作为扩展用于C++代码。如果您想拒绝该代码,请尝试使用-std=standard-ansi-pedantic选项进行实验。


2
“-std”和“-ansi”选项对此扩展没有任何影响。 - Jonathan Wakely

7

6

它是有效的C99,但不是有效的C ++。这是两种语言之间不少差异之一。


2
我猜这将在C++0x中得到支持。 - Mehrdad Afshari
不符合草案标准的8.3.4部分。 - anon
3
它将永远不会包含在C++1x中:D 但让我们希望dynarray<T>能够加入。我很喜欢它。这样你就可以使用 dynarray<int> a(some_size); 来高效地分配内存,可能需要一些编译器技巧,如_alloca等。 - Johannes Schaub - litb
3
针对未来的任何人:这将被包含在 C++14 中(其中包括 typedefsizeof() 等较小的功能集)。 - Red XIII
2
@RedXIII:猜错了。VLA和dynarray都不是C++14的一部分。 - magras
@JohannesSchaub-litb有计划在标准的后续版本中引入它吗? - St.Antario

2

这段代码在GNU GCC编译器中运行。

#include<bits/stdc++.h>

int main(int argc, char **argv)

{
    size_t size;

   std:: cin >> size;

    int array[size];

    for(size_t i = 0; i < size; i++)

{

array[i] = i;

        std:: cout << i;

 }

    return 0;
}

1

最近我遇到了一个需要使用堆栈分配数组的情况。(这是一个对v8的包装器,每次方法调用需要一个args数组)。

std::vector会进行堆内存分配,其性能不可接受。

这里是我的解决方案,使用模板来分配case数组:

template<size_t Argc>
static void call(...) {
    v8::Local<v8::Value> v8Args[Argc];

    // use v8Args
    ...
}

template<typename It>
static void callV8Function(size_t argc, It argvBegin, It argvEnd,) {
    // C++ don't have dynamic stack allocation (like C99 does)
    // try to avoid heap-allocation...
    if (argc <= 4) {
        return callV8FunctionOnStack<4>(...);
    } else if (argc <= 8) {
        return callV8FunctionOnStack<8>(...);
    } else if (argc <= 16) {
        return callV8FunctionOnStack<16>(...);
    } else if (argc <= 32) {
        return callV8FunctionOnStack< 32>(...);
    } else {
        std::vector<v8::Local<v8::Value>> v8Args(argc);
        // fallback to vector
   }
}

(当然,我可以只使用一个大小为32的数组,但这不够优雅。)

1

如果你使用Dev-Cpp编译器,你可以动态地给数组分配大小。我尝试过并没有出现错误,但在Visual C++和Visual Studio编译器中是不可能的。 我认为原因是Dev-C++会将未初始化的int赋予一个正数,当我们给它一个数字时,它会被替换为给定的数字。 但其他编译器可能会给未初始化的变量赋予null。


大多数编译器不会为未初始化的本地变量分配任何值,它们通常会显示出占用它们的内存中原有的内容,直到它们被程序分配。似乎你提到的Dev-C++是建立在MinGW之上的IDE,它包含了GCC编译器的一个移植版本。正如其他答案所指出的那样,可变长度数组(VLAs)不是标准的C++,但一些编译器(包括g++)仍支持它们。 - jerry
Dev-C++不是编译器,Visual Studio也不是。Dev-C++使用GCC/G++作为其编译器,而Visual Studio使用cl(Microsoft的编译器后端)。Dev-C++和Visual Studio本身都是集成开发环境(IDE),这是一个重要的区别。Dev-C++不会“分配”任何东西,编译器会这样做。 - Elkvis

0

变长数组(VLAs)在C++14标准中得到支持,该标准最近已被接受,正在等待出版。


2
可变长度数组(VLAs)仍未成为标准的一部分。有一个dynarray TS,但目前尚未批准。 - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接