C语言内存布局

关注微信公众号塔容万物

进程内存空间

Linux中使用虚拟内存技术,将进程内存空间分为两部分:内核空间和用户空间。

----+------------------+
 1G |                  | 内核空间(与其他进程共享)
----+------------------+
    |                  |
 3G |                  | 用户空间(进程私有)
    |                  |
----+------------------+

内核空间是操作系统的私有空间,用户空间是进程的私有空间。运行在用户空间的进程不可以访问内核空间。C程序运行在用户空间中,根据内存分配方式,用户空间又分为以下几个区域。

+------------------+ 高地址
|        ...       | 参数和环境变量
+------------------+
|       stack      | 栈区 向下增长
+------------------+
|       mmap       | 内存映射区(动态链接库...)
+------------------+
|       heap       | 堆区 向上增长
+------------------+
|       bss        | 未初始化的全局变量
+------------------+
|       data       | 已初始化的全局变量
+------------------+
|       rodata     | 只读数据区
+------------------+
|       text       | 代码区
+------------------+ 低地址

栈区(局部变量)

局部变量和函数堆栈帧都在栈区,栈区向下增长,栈区的大小是固定的,一般为8M。

// a, b, c都是栈区的局部变量
int f(int a, int b) {
    int c = a + b;
    return c;
}

堆区

使用malloc申请的变量在堆区,堆区向上增长,堆区的大小是不固定的,一般为1G。

// p是堆区的变量
int* p = (int*)malloc(sizeof(int) * 10);

全局变量

全局变量在data、bss、rodata区。其中data区存放已初始化的全局变量,bss区存放未初始化的全局变量,rodata区存放只读的全局变量。

static int x = 1;      // data区   静态全局变量
int a = 1;             // data区   全局变量
int b;                 // bss区    全局变量
const int c = 2;       // rodata区 只读全局变量

int f() {
    static int d = 3;  // data区   静态局部变量
    static int e;      // bss区    静态局部变量
    return 0;
}

生命周期

存储在不同位置的变量,其生命周期也不相同

存储位置生命周期
寄存器由编译器决定
栈区函数调用时创建,函数返回时销毁
堆区由程序员决定
data区程序启动时创建,程序结束时销毁
bss区程序启动时创建,程序结束时销毁
rodata区程序启动时创建,程序结束时销毁
text区程序启动时创建,程序结束时销毁

作用域

全局变量和静态变量的作用域是整个文件,局部变量的作用域是函数内部。

计数器

int counter()
{
  // count是局部静态变量,函数返回时不会销毁
  static int count = 0; // data区
  count++;
  return count;
}

int main()
{
  printf("%d\n", counter()); // 1
  printf("%d\n", counter()); // 2
  printf("%d\n", counter()); // 3
  return 0;
}