当一个C程序被编译为可执行程序,我们运行这个可执行程序时,系统会为这个程序分配一个虚拟内存,以下就是其分配详情(不同的操作系统分的内存大小可能不同,但是顺序都是一样的,只不过每个区域的大小不一)

高地址  ┌─────────────────────┐  0xFFFFFFFF
        │     内核空间        │  (操作系统占用,用户程序无法访问)
        │  (1GB,供内核使用) │
        ├─────────────────────┤  0xC0000000(3GB 边界)
        │                     │
        │       栈区 ↓        │  ← 向下增长(从高地址向低地址)
        │  (局部变量、形参)  │
        ├─────────────────────┤
        │                     │
        │       空洞          │  ← 栈和堆之间的大片空闲区域
        │    (可动态调整)    │
        │                     │
        ├─────────────────────┤
        │       堆区 ↑        │  ← 向上增长(从低地址向高地址)
        │ (malloc/calloc 分配)│
        ├─────────────────────┤
        │        .bss段       │  ← 未初始化的全局/静态变量(默认值为0)
        │                     │
        ├─────────────────────┤
        │        .data段      │  ← 已初始化的全局/静态变量(有初始值)
        │                     │
        ├─────────────────────┤
        │        .rodata段    │  ← 只读数据(字符串常量、const 修饰的变量)
        │                     │
        ├─────────────────────┤
        │       .text段       │  ← 代码段(程序的机器指令,只读)
        │   (程序指令/机器码) │
        │                     │
低地址  └─────────────────────┘  0x08048000(Linux 默认加载地址)

1. 代码段(.text,只读)

  • 存放内容:程序的机器指令(CPU 执行的二进制代码)

  • 权限只读(防止程序意外修改自己的指令)

  • 生命周期:程序启动到结束,一直存在

  • 举例:你写的 printf("hello"); 编译后就在这里

// 函数体编译后存放在 .text 段
void func() {
    int a = 10;  // 函数指令在这里,但变量 a 在栈上
}

2. 只读数据段(.rodata,常量区)

  • 存放内容:字符串常量、const 修饰的全局变量

  • 权限只读(修改会导致段错误)

  • 生命周期:程序启动到结束,一直存在‘

char *str = "hello";   // "hello" 存放在 .rodata 段
const int num = 100;   // num 存放在 .rodata 段(全局 const)

3. 数据段(.data,已初始化数据段)

  • 存放内容:已经初始化的全局变量、静态变量(初始值不为 0)

  • 权限可读写

  • 生命周期:程序启动到结束,一直存在

int global = 100;      // 存放在 .data 段
static int count = 5;  // 存放在 .data 段

4. BSS 段(.bss,未初始化数据段)

  • 存放内容:未初始化的全局变量、静态变量(默认值为 0)

  • 权限可读写

  • 生命周期:程序启动到结束,一直存在

  • 特性:程序加载时,操作系统会把 BSS 段全部清零

int global2;           // 存放在 .bss 段(默认值为 0)
static int count2;     // 存放在 .bss 段(默认值为 0)

5. 堆区(Heap,动态分配)

  • 存放内容malloccallocrealloc 分配的内存

  • 特点

    • 手动分配和释放malloc + free

    • 从低地址向高地址增长sbrk/mmap

    • 空间较大,但分配速度比栈慢

    • 如果不释放,会造成内存泄漏

int *p = malloc(100 * sizeof(int));  // 在堆上分配 400 字节
// ... 使用 ...
free(p);  // 手动释放

6. 栈区(Stack,函数调用)

  • 存放内容:局部变量、形参、函数返回值、函数调用信息(栈帧)

  • 特点

    • 自动分配和释放(函数调用时压栈,返回时弹栈)

    • 从高地址向低地址增长(向下增长)

    • 空间有限(通常默认 8MB,超出会栈溢出)

    • 分配速度极快(移动栈指针即可)

void func(int a, int b) {   // a 和 b 在栈上
    int x = 10;             // x 在栈上
    char str[100];          // str 在栈上
}   // 函数返回后,x、str、a、b 全部被自动回收

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐