全局和局部变量的内存分配
Posted
技术标签:
【中文标题】全局和局部变量的内存分配【英文标题】:Memory allocation for global and local variables 【发布时间】:2012-08-12 05:18:39 【问题描述】:我了解到全局变量的内存是在程序启动时分配的,而局部变量的内存是在函数调用时分配的。
案例 1: 我声明了一个大小为 63500000 的全局整数数组,使用的内存为 256 MBIdeone Link
include <stdio.h>
int a[63500000];
int main()
printf ("This code requires about 250 MB memory\n");
return 0;
案例 2: 我在 main() 中声明了一个相同大小的本地整数数组,使用的内存为 1.6 MBIdeone 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 MBIdeone Link
#include <stdio.h>
void f()
int a[63500000];
int main()
f();
return 0;
请解释为什么使用的内存有差异或者我的内存分配概念是错误的??
【问题讨论】:
您知道,您应该直接在答案中发布代码,而不是提供 Ideone 链接 您如何知道这是程序消耗的内存量? 在您的本地数组示例中,您实际上并未使用大部分数组,因此编译器可以安全地对其进行优化。 @ArjunShankar 先生,每当我们在 ideone 中编译程序时,它都会显示程序所需的时间和使用的内存。 @nos - 谢谢。我现在明白了。 【参考方案1】:案例 2、3
您在函数内部定义的变量在堆栈上分配。这意味着当函数退出时,相关的内存被清理(堆栈被“弹出”)。
案例 1
在全局范围内定义的变量分配在一个数据段(或者,通常是从操作系统请求的内存空间)中,该数据段存在于进程的生命周期中。
另外
使用 malloc 分配的内存是从堆中分配的,并且在使用 free 显式释放之前一直保持分配状态。
请注意,现代操作系统可能会提供程序请求的地址空间,但不会在内存(或通常称为 page 的内存的一部分)在物理上用 RAM 支持该地址空间物理访问。
【讨论】:
如果我在 case 2 和 case 3 发生分段错误时尝试执行 memset(a, 0, sizeof(a))。为什么? 关于情况 2 和 3,堆栈上根本没有分配内存。曾经。它被 GCC 简单地优化掉了。您可以在这里看到原因:ideone.com/1a7A1 即 OP 的 250MB 数组示例和这个 4 字节示例,都使用几乎相同数量的内存,正如 ideone 报告的那样。【参考方案2】:case 2
和 case 3
会导致堆栈溢出,因为您要求 64 MB 堆栈内存,其中您的堆栈是在 Linux 上通常为 8 MB。这将导致随机的坏事和/或核心转储和崩溃。
this 的回答很好地解释了进程地址空间的各个部分(.text、.bss、.data)以及如何完成变量的各种分配。
【讨论】:
【参考方案3】:首先: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 网站失败的原因是他们在执行程序时限制了堆栈大小(他们应该这样做,否则恶意用户可能会弄乱他们的系统)。
【讨论】:
如果我在 case 2 和 case 3 中尝试做 memset (a, 0, sizeof(a)),为什么会抛出 SIGSEV 异常? @Snehasish - 看看我回答的最后一点。我已经编辑过了。这是因为堆栈大小限制(这是操作系统设置的限制) +1 用于解释优化后的版本,不会导致堆栈溢出。以上是关于全局和局部变量的内存分配的主要内容,如果未能解决你的问题,请参考以下文章