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

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 2case 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 用于解释优化后的版本,不会导致堆栈溢出。

以上是关于全局和局部变量的内存分配的主要内容,如果未能解决你的问题,请参考以下文章

全局变量和局部变量在内存中的区别

C语言中的 局部变量,存储在啥地方?

C++中的全局变量普通局部变量和静态局部变量的区别

C_局部变量&全局变量

全局变量与局部变量在内存中的区别详细解析

C语言局部变量和全局变量的区别。——Arvin