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

Sne*_*ish 10 c memory-management

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

情况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;
}
Run Code Online (Sandbox Code Playgroud)

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

#include <stdio.h>
void f()
{
    int a[63500000];
}

int main()
{
    f();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

Arj*_*kar 24

首先:ideone编译器是GCC.

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

void foo ()
{
  int a[63500000];
}
Run Code Online (Sandbox Code Playgroud)

gcc -S -O2 foo.c 产生:

foo:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    ret
Run Code Online (Sandbox Code Playgroud)

没有被分配在栈上,在所有.

该阵列由GCC简单优化,因为它从未使用过.

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

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

int bar (int a, int b, int c)
{
  int f[63500000];
  f[a] = 9;
  f[b] = 7;
  return f[c];
}
Run Code Online (Sandbox Code Playgroud)

情况非常不同:

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
Run Code Online (Sandbox Code Playgroud)

该行: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);
}
Run Code Online (Sandbox Code Playgroud)

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

ulimit -s 1000000 #i.e. allow stack size to grow close to 1GB
Run Code Online (Sandbox Code Playgroud)

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

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


Eri*_* J. 6

案例2,3

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

情况1

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

另外

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

需要注意的是一个现代化的操作系统可能提供由程序请求的地址空间,但不是身体回来的RAM的地址空间,直到内存(或通常称为内存的部分页面)的物理访问.