动态分配数组和静态数组的区别

Posted

技术标签:

【中文标题】动态分配数组和静态数组的区别【英文标题】:Difference between dynamically allocated arrays and static arrays 【发布时间】:2020-09-18 06:50:01 【问题描述】:

我正在了解动态内存分配,并且我了解到动态数组(当您不知道用户需要多少个元素/声明用户需要的大小的数组时使用像 int *p = new int[n] \\ where n is the user input describing the number of elements 这样的数组)想要。但是,与其那样做,我们为什么不能像这样声明一个静态数组:

int n;
    cout << "Enter the size : " <<endl;
    cin>>n;
    int a[n];

那么在这种情况下动态数组有什么优势呢?我不理解这个概念。

编辑:感谢您的回答。一些用户回应说不允许通过键入 a[n] 来声明数组。但是,为什么我输入以下代码时我的程序运行良好:

int main()
    int n;
    cout << "Enter the size : " <<endl;
    cin>>n;
    int a[n];
    cout << "Enter your numbers : " <<endl;
    for (int i=0;i<=n;i++)
        cin>>a[i];
    
    for (int i=0;i<=n;i++)
        cout<<a[i]<<endl;
    

【问题讨论】:

int a[n]; -- 这不是有效的 C++。 【参考方案1】:

但是,我们为什么不能像这样声明一个静态数组,而不是这样做:

因为语言规则是这样说的。在 C++ 中,所有变量的大小必须在编译时就知道。

请注意,如果您声明这样的数组(如果在编译时已知大小),则该数组将具有自动存储,而不是静态存储。

那么在这种情况下动态数组有什么好处呢?

优点是动态数组的大小不需要在编译时知道。

此外,自动存储的数量通常非常有限,而动态存储则不然。使用自动存储创建大型数组很有可能导致堆栈溢出。

【讨论】:

但是当我编写以下代码时,程序运行良好。为什么会这样?int main() int n; cout >n;整数 [n]; cout >a[i]; for (int i=0;i @Anon123 该程序格式错误。当一个格式错误的程序编译时,通常要么 1. 编译器实现语言扩展,要么 2. 编译器损坏。在这种情况下,它是 1。如果编译器没有发出诊断消息,那么它不符合 C++ 标准。 有时运行良好的程序不是合法的 C++。再举一个例子,从for (int i=0;i&lt;=n;i++) 上面的代码看这个应该是for (int i=0;i&lt;n;i++)。工作正常绝对不能保证正确。【参考方案2】:

现在,人们已经声明int a[n] 不是有效的c++。但也许我可以为您提供您正在寻找的答案。

那么在这种情况下动态数组有什么好处呢?

语法int a[n] 称为VLA(可变长度数组)。这些在 C++ 中是非法的,但在 C 中是允许的。所以让我们关注技术差异,或者说 VLA 的缺点。

让我们先把显而易见的事情弄清楚。 C89 及之前没有 VLA,因此动态分配是分配可变长度内存的唯一方法。

还有一件事,静态数组甚至 VLA 都分配在堆栈上(虽然这是implementation defined,但通常它会在堆栈上)。而动态数组是在堆上分配的。有关堆栈和堆的更多信息,请阅读this

现在,C++ 中禁止 VLA 是有充分理由的。 VLA 可能会导致各种未定义的行为,应该始终避免,除非您确切知道自己在做什么。 “你确切地知道你在做什么”,我的意思是你知道那个 VLA 的 size 参数不会溢出堆栈。

假设 C++ 中允许使用 VLA,您的代码中的这一行-

cin>>n;
int a[n];

如果用户输入大量的n,远远超过堆栈大小怎么办?这是有保证的堆栈溢出。注意到问题了吗?与堆相比,堆栈非常小。这也解释了here 和here

是不惜一切代价避免使用 VLA 的主要原因。尽管 VLA 实际上比前面提到的更繁荣。事实上,我总是随身携带与 VLA 相关的 UB 列表,只是 有那么多问题。

回到我的观点

应该始终避免使用 [VLA],除非您确切知道自己在做什么

老实说,你应该永远使用 VLA,你真的不能,因为那甚至不是标准的 C++。但是堆栈分配通常比堆分配快。虽然不是出于人们可能认为显而易见的原因。阅读this。所以有时,如果您使用 C(不是 C++),唯一安全使用 VLA 的时间是当您知道int a[n]n 的最大大小不 溢出堆栈并且 VLA 的声明位于您当前声明它的范围的顶部。 alloca(在 c99 之前是使用 VLA 的唯一方法)的创建者似乎同意。

摘自here-

你可以使用 alloca() 的形式:

pointer_variable = alloca(表达式);

作为函数最外层块中的表达式语句。

哦,只是为了回答您的编辑:

感谢您的回答。一些用户回应说不允许通过键入 a[n] 来声明数组。但是,为什么当我输入以下代码时我的程序运行良好:

这是因为您的编译器允许这样做。但请记住,标准没有。所以这些东西可以产生好的“它在我的机器上工作!”

【讨论】:

感谢您的精彩解释!【参考方案3】:
int a[n];

这一行声明了一个可变长度数组,但这不是 C++ 的一部分。一些编译器将其实现为扩展,但此时它实际上并不是 C++。这将是特定编译器的 C++ 方言。

因此,这甚至不是一个选项(如果您希望您的代码可移植)。

标准建议是使用std::vector&lt;int&gt; 而不是管理自己的内存,所以我建议这样做。 (当然,练习帮助你理解如何进行正确的内存管理并没有错。)

【讨论】:

以上是关于动态分配数组和静态数组的区别的主要内容,如果未能解决你的问题,请参考以下文章

C++数组在内存中的分配

C++ 中的静态数组与动态数组

如何分配动态静态多维数组

Java静态内存与动态内存分配的解析

具有大小动态分配和预定大小的简单数组内存分配

获取动态分配的数组大小