全局变量和局部变量的内存分配

我已经知道全局变量的内存是在程序启动时分配的,而局部变量的内存是在进行函数调用时分配的。

情况1:
我声明了一个大小为63500000的全局整数数组,使用的内存为256 MB
Ideone Link

include  int a[63500000]; int main() { printf ("This code requires about 250 MB memory\n"); return 0; } 

案例2:
我在main()中声明了一个大小相同的本地整数数组,使用的内存是1.6 MB
Ideone链接

 #include  int main() { int a[63500000]= {1,5,0}; printf ("This code requires only 1.6 MB \n"); //printf ("%d\n", a[0]); return 0; } 

案例3:
我在另一个函数中声明了一个大小相同的本地整数数组,使用的内存是1.6 MB
Ideone Link

 #include  void f() { int a[63500000]; } int main() { f(); return 0; } 

请解释为什么使用的内存有差异或者我的内存分配概念是错误的?

首先:ideone编译器是GCC。

那么,GCC在编译时会做什么?:

 void foo () { int a[63500000]; } 

gcc -S -O2 foo.c生成:

 foo: pushl %ebp movl %esp, %ebp popl %ebp ret 

根本没有在堆栈上分配任何东西

该arrays由GCC简单优化,因为它从未使用过。

GCC不会对全局执行此操作,因为有可能在另一个编译单元中使用全局,因此不确定它是否从未使用过。 另外:全局不在堆栈中(因为它是全局的)。

现在,让我们看看实际使用本地数组时会发生什么:

 int bar (int a, int b, int c) { int f[63500000]; f[a] = 9; f[b] = 7; return f[c]; } 

情况非常不同:

 bar: pushl %ebp movl %esp, %ebp subl $254000000, %esp movl 8(%ebp), %eax movl $9, -254000000(%ebp,%eax,4) movl 12(%ebp), %eax movl $7, -254000000(%ebp,%eax,4) movl 16(%ebp), %eax movl -254000000(%ebp,%eax,4), %eax leave ret 

此行: subl $254000000, %esp对应于数组的大小。 即在堆栈上分配内存。

现在,如果我尝试在程序中使用bar函数,该怎么办:

 int bar (int a, int b, int c) { int f[63500000]; f[a] = 9; f[b] = 7; return f[c]; } int main (void) { return bar (0, 0, 0); } 

我们已经看到, bar函数在堆栈上分配250左右的兆字节。 在我的默认GNU / Linux安装中,堆栈大小限制为8MB。 因此,当程序运行时,会导致“分段错误”。 如果我愿意,可以通过在shell中执行以下命令来增加它:

 ulimit -s 1000000 #ie allow stack size to grow close to 1GB 

然后我可以运行该程序,它确实会运行。

它在ideone网站上失败的原因是它们在执行程序时限制了堆栈大小(并且它们应该,否则恶意用户可能会搞乱他们的系统)。

案例2,3

您在函数内定义的变量将在堆栈中分配。 这意味着当函数退出时,相关的内存被清除(堆栈被“弹出”)。

情况1

在全局范围中定义的变量在数据段(或通常是从操作系统请求的存储空间)中分配,该数据段在进程的生命周期中存在。

另外

使用malloc分配的内存是从堆中分配的,并保持分配,直到使用free明确释放。

注意,现代OS可能很好地提供程序所请求的地址空间,但是在物理上访问存储器(或通常称为页面的存储器的一部分)之前,物理上不用RAM支持该地址空间。

case 2case 3会导致堆栈溢出,因为您要求64 MB的堆栈内存,其中您的堆栈通常在Linux上为8 MB 。 这会导致随机坏事和/或核心转储和崩溃。

这个答案极大地解释了进程地址空间的各个部分(.text,.bss,.data)以及如何进行各种变量分配。