当我可以从静态实现相同的目标时,为啥我需要使用动态内存分配?
Posted
技术标签:
【中文标题】当我可以从静态实现相同的目标时,为啥我需要使用动态内存分配?【英文标题】:Why do I need to use dynamic memory allocation when I can achieve the same from static?当我可以从静态实现相同的目标时,为什么我需要使用动态内存分配? 【发布时间】:2021-02-02 03:32:48 【问题描述】:让我举个例子来解释我的问题,
案例一
#include<iostream>
using namespace std;
int main()
int n;
cin>>n;
int a[n];
for(int i=0;i<n;i++)
cin>>a[i];
案例二
#include<iostream>
using namespace std;
int main()
int n;
cin>>n;
int *a = new int[n];
for(int i=0;i<n;i++)
cin>>a[i];
如果我错了,请纠正我,据我了解,Case I属于静态内存分配域,Case II属于动态内存分配域。因此,如果我能够通过静态内存分配实现相同的功能,为什么要使用动态。
在上述两种情况下,我都能够实现相同的功能,但是为什么 Case I 被认为不好,而 Case II 是正确的方法。
这两个代码的唯一区别是第 6 行。
【问题讨论】:
不行,情况1是栈内存分配,还有is a non-standardg++
extension,所以最好不要用,IMO。
Case I 不被认为是“坏的”。它根本行不通。编译器不会理解你的。该语言本可以设计为为您进行动态分配,但他们却选择强制您使动态分配更加明确。
尝试编译两者,输入100000000
,看看哪个崩溃。
@Yksisarvinen 我尝试了两个输入大小为 100000000,case II 运行良好,但 case I 崩溃了。我确信这是因为在 case I 中我们从堆栈中获取内存,这是一种稀缺资源,而 case II 使用大量可用的堆内存。跨度>
没错。有两件事需要考虑:便携性和可用内存。对于便携性问题,请参阅下面的答案。案例 I 是非标准的,只有某些编译器接受它。如果您不关心这一点,因为您确信您的代码只会使用一个编译器,那么您的下一个问题是这两个区域的可用内存。堆栈是否足以供您使用?根据这些,您可以选择适合您的解决方案。
【参考方案1】:
案例一属于静态内存分配域,案例二属于动态内存分配域。
这个假设是错误的。您正在使用的非标准功能与这样的 sn-p;
int n;
// determin n at runtime ...
int a[n];
被称为 VLA(可变长度数组)(有关更多详细信息,请参阅 this thread),并且是一种隐藏内存分配的有争议的方式(可能在堆栈上,请参阅 @André 的评论)并最终以方便的语法进行清理。
请注意,如果没有非标准 VLA 扩展,当编译时不知道最大数组维度时,您将无法使用堆栈空间中的数组。工作示例:
#include <array>
constexpr std::size_t N = 42; // known at compile time
std::array<int, N> data; // allocated on the stack
【讨论】:
您可能应该说“当 最大 数组维度未知时”。静态分配最大值并仅使用需要的部分是很常见的。 轻微吹毛求疵:vla 不会“隐藏动态内存分配”。对于 VLA,它通常位于堆栈中的某个位置,更像是一个可变大小的堆栈段。例如:***.com/q/31199566/417197【参考方案2】:案例 1 不 执行"static" memory allocation,而是memory allocation "on stack"。这是variable length array。
有多种原因:
可变长度数组是一个编译器扩展。它们不是 C++ 的一部分。
可变长度数组没有错误处理。向用户转发任何有意义的错误消息是不可能的,而且调试此类程序非常困难。通常,该过程只会显示不友好的“分段错误”错误消息。
分配的最大内存将非常非常小,并且取决于代码的其他部分(使调试非常困难)。大多数Linux将堆栈限制设置为8Mb。分配更多将不会出错,而是当写入超过该阈值的内存位置时,进程将收到分段错误信号。您始终可以为您的进程设置更大的堆栈限制。
内存必须在块的末尾被释放。不可能从函数返回这样的内存并在其范围之外使用它,这使得它对于大多数使用动态内存的应用程序来说毫无用处。
【讨论】:
关于“操作系统更难管理分配的内存”:什么?操作系统不在乎。堆栈中的页面与其他页面一样是虚拟内存。它们可以单独交换到磁盘,也可以不交换。Pages in the stack are virtual memory just like other pages
我不知道。
好的,这是一个有趣的事实。内存管理可用于防止堆栈中的某些地址/指针错误。堆栈可能是 8 MiB,但到目前为止堆栈指针可能只有 1 MiB,系统可能知道分配了 8 MiB 的虚拟地址空间,但到目前为止只映射了 1 MiB 使用的部分。当进程尝试超过 1 MiB 的内存访问时,它会导致硬件陷阱,操作系统可以查看它来决定做什么。如果是读访问,操作系统可以说这是一个错误,内存没有被初始化,……
... 它可以拒绝映射内存并向进程传递信号。如果是写访问,操作系统可以查看它在哪里。如果它只是超过 1 MiB,系统可以说,好吧,你正在增加堆栈,我将映射更多内存并让进程继续。如果超过 1 MiB,系统会说,哇,那是一个奇怪的跳转,你一定是搞错了,我不会映射内存,但会向进程发送信号。
VAX/VMS 曾经具有后一个功能:如果您在使用堆栈时尝试跳得太远,而不是以“正常”数量增长堆栈帧,则进程会崩溃。这成为支持可变长度数组的问题,其中有人试图在堆栈上创建一个大数组并开始写入其中的某些部分。必须修改编译器,以便在创建大型可变长度数组时,编译器生成对每个页面中一个元素的令牌写访问,以使堆栈以操作系统的速度增长接受。【参考方案3】:
正如@lubgr 解释的那样,不可能分配在编译时未确定的静态内存(在堆栈中)。 所以如果你想在运行时确定内存,你应该使用动态内存分配(堆)。
此外,正如@Jeff Hill 在Heap vs Stack 帖子中所解释的那样,堆大小在运行时是动态的,而堆栈大小是静态的(因此,即使可以在堆栈中分配运行时变量内存,有时您的应用程序也会面临堆栈溢出)。
另一个不同的是速度; Stack 比 Heap 快(因为它们的访问模式)
【讨论】:
是自动内存,不是静态的,“不可能”应该是“C++标准不支持”。当使用支持它的编译器时,可以通过编译器扩展来实现。此外,“堆”是用词不当;用于管理动态内存的内存结构不一定是堆。以上是关于当我可以从静态实现相同的目标时,为啥我需要使用动态内存分配?的主要内容,如果未能解决你的问题,请参考以下文章