我已经知道全局变量的内存是在程序启动时分配的,而局部变量的内存是在进行函数调用时分配的。
案例1:
我已声明一个大小为63500000的全局整数数组,使用的内存为256 MB
Ideone Link
include <stdio.h>
int a[63500000];
int main()
{
printf ("This code requires about 250 MB memory\n");
return 0;
}
案例2:
我在main()中声明了一个大小相同的本地整数数组,使用的内存是1.6 MB
Ideone link
#include <stdio.h>
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 <stdio.h>
void f()
{
int a[63500000];
}
int main()
{
f();
return 0;
}
请解释为什么使用的内存有差异或者我的内存分配概念是错误的?
答案 0 :(得分:24)
首先:ideone编译器是GCC。
那么,GCC在编译时会做什么?:
void foo ()
{
int a[63500000];
}
gcc -S -O2 foo.c
生成:
foo:
pushl %ebp
movl %esp, %ebp
popl %ebp
ret
即。 没有在堆栈上分配,在所有。
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 #i.e. allow stack size to grow close to 1GB
然后我可以运行该程序,它确实会运行。
它在ideone网站上失败的原因是它们在执行程序时限制了堆栈大小(并且它们应该,否则恶意用户可能会搞乱他们的系统)。
答案 1 :(得分:5)
案例2,3
在函数内定义的变量在堆栈上分配。这意味着当函数退出时,相关的内存被清除(堆栈被“弹出”)。
案例1
全局范围中定义的变量在数据段(或通常是操作系统请求的内存空间)中分配,该数据段在进程的生命周期内存在。
<强>另外强>
使用 malloc 分配的内存是从堆中分配的,并且在使用 free 明确释放之前一直保持分配。
请注意,现代操作系统可能会提供程序请求的地址空间,但不会在物理上支持带RAM的地址空间,直到内存(或内存的一部分通常称为页)为止实际访问。
答案 2 :(得分:2)
case 2
和 case 3
会导致堆栈溢出,因为您要求 64 MB 堆栈内存其中您的堆栈在Linux上通常 8 MB 。这会导致随机坏事和/或核心转储和崩溃。
this回答极大地解释了进程地址空间的各个部分(.text,.bss,.data)以及如何进行各种变量分配。