C++ 自动/静态内存分配

C++ 自动/静态内存分配,c++,c,memory-management,operating-system,virtual-memory,C++,C,Memory Management,Operating System,Virtual Memory,也许是个天真的问题,但是 确认或拒绝: 自动和静态存储持续时间的对象/变量内存的存在取决于编译时,程序运行时失败的可能性绝对为零,因为没有足够的内存用于自动对象 当然,当自动对象的构造函数执行动态分配,这种分配失败时,我们认为这是动态分配失败,而不是自动的。自动分配肯定会失败——这通常被称为堆栈溢出。当有人试图将一个大数组作为局部变量时,您经常会看到这种情况。无界(或不够有界)递归也会导致这种情况 以独立于平台的方式,您无法真正做到的是检测自动分配失败并加以处理。不是这样。自动分配可能会导致堆栈

也许是个天真的问题,但是

确认或拒绝:

自动和静态存储持续时间的对象/变量内存的存在取决于编译时,程序运行时失败的可能性绝对为零,因为没有足够的内存用于自动对象


当然,当自动对象的构造函数执行动态分配,这种分配失败时,我们认为这是动态分配失败,而不是自动的。

自动分配肯定会失败——这通常被称为堆栈溢出。当有人试图将一个大数组作为局部变量时,您经常会看到这种情况。无界(或不够有界)递归也会导致这种情况


以独立于平台的方式,您无法真正做到的是检测自动分配失败并加以处理。

不是这样。自动分配可能会导致堆栈溢出,这会导致我所知道的大多数体系结构/平台上的进程立即终止

此外,程序可能无法从底层平台为静态变量分配足够的空间,在这种情况下,程序仍将失败,但在调用
main
之前它将失败。

两个字:P

简单反例:

#include <string.h>

int main()
{
    int huge[0x1FFFFFFF]; // Specific size doesn't matter;
                          // it just has to be bigger than the stack.

    memset(huge, 0, sizeof(huge) / sizeof(int));

    return 0;
}
#包括
int main()
{
int巨型[0x1ffffff];//具体大小无关紧要;
//它必须比堆栈大。
memset(巨大,0,sizeof(巨大)/sizeof(int));
返回0;
}
示例:

#include <iostream>

using namespace std;

class A
{
public:
    A() { p = new int[0xFFFFFFFF]; }

private:
    int* p;
};

static A g_a;

int main()
{
    cout << "Why do I never get called?" << endl;
}
#包括
使用名称空间std;
甲级
{
公众:
A(){p=newint[0xFFFFFFFF];}
私人:
int*p;
};
静态A g_A;
int main()
{

cout在具有Overmit的系统上(例如默认配置中的Linux),静态存储持续时间的对象甚至可能在运行时导致失败。在程序启动时,这些对象将存在于写入零页上的任一副本中(如果未初始化)或磁盘上可执行文件的写时复制映射。在第一次尝试写入时,将发生页面错误,内核将为您的进程创建本地可修改副本。如果内核不小心,并且没有保留尽可能多的内存,这可能会失败,其结果将是可怕的OOM杀手。

没有健壮的系统存在此问题,Linux行为可以通过以下方式修复:

echo "2" > /proc/sys/vm/overcommit_memory

我不得不说,我简直不敢相信这个答案有两张赞成票。然后我得到了三张。@Prasony:我怎么能放弃呢?!:)@Prasony:很抱歉我错发了。我会安排一个约会,下周把脚从嘴里拿开:)堆栈框架大小通常是无限的。不过堆栈本身的总大小通常是有限的“@Armen:它应该编译,但在运行时失败得惊人。(虽然编译器可能会看它并说“你疯了吗?”)如果你试图在32位机器上编译它,它将失败,因为20亿个整数比机器的整个内存空间大(大约2倍)@Armen:好了,现在它可以自己编译了。@Billy:嘿,你说得对。我忘了我用的是64位机器。@R:哎呀,你说得对。结果当然没有变化。这就是编写错误程序的问题。你错过了OP问题的最后一句话。“当然,当自动对象的构造函数执行动态分配和分配失败时,我们认为这是动态分配失败,而不是自动的。“编辑:和
使用名称空间std;
需要消亡!:P
在简单的示例中使用名称空间std;
几乎不是问题。它使示例比在任何地方都使用
std::
更具可读性。当然,在这个示例中,这并不重要,因为我只使用了1
cout
语句(一个在失败发生之前从未运行过的变量)。此外,他的问题是关于静态变量的创建。我使用了
new
,但是您可以像
int p[0x7fffff]一样轻松地创建成员变量
以获得相同的效果。在任何一种情况下,它都会导致堆栈溢出。@Zac:1.我认为这会降低代码的可读性。当您调用标准库时,在调用站点上应该很明显。2.不,无论是成员变量还是对
new
的调用都不会导致堆栈溢出。第一种情况可能会失败,因为静态存储空间将耗尽。对
new
的调用失败,因为堆已耗尽。分配都不会涉及堆栈。曾经。@Billy 1。这是一个宗教讨论,可能会一直持续到审判日,所以只需说,每个人都有自己的。2.您忘记了堆栈溢出错误的全文:堆栈溢出堆。也就是说,如果您分配了太多的堆空间,它将与您的堆栈空间相交(与静态存储空间相同),因此,在任何一种情况下,它都会导致相同的问题:您在任何给定的内存池中分配了太多的内存,并损坏了其他内存池。@Zac:这根本不是真的。简化“堆栈以一种方式增长,堆以另一种方式增长"在大多数真实机器上不是这样,因为大多数真实机器都有虚拟内存。是的,内存边界已溢出,但这不会使其成为堆栈溢出。只有自动变量存储在堆栈上,而没有自动变量。如果幸运的话,它会导致即时终止。如果不幸的话,它会导致其他堆栈溢出内存的一部分,并为攻击者提供根目录。:-)幸运的是,只有在实际分配的内存非常大(许多kb)的情况下,才会出现后者对象,但不安全地使用C99 VLA可能会导致这种情况。@R.Hmm…不确定其他平台如何处理此问题。至少在Win32上,它会终止进程。在其他框中,它可能不会。@Billy:如果每次调用只会使堆栈使用量增加几kb,您将点击一个保护页,进程将