目录

程序员的自我修养链接装载与库-对书中常见段的讲解总结

《程序员的自我修养—链接、装载与库》– 对书中常见段的讲解总结

1. 核心段的作用与特点

(1) .text 段(代码段)
  • 内容 :存放程序的 可执行指令 (机器码),例如函数的实现代码。
  • 特点
    • 通常是 只读 的(防止程序意外修改指令)。
    • 在程序运行前已确定大小,多个进程可共享同一份代码段(节省内存)。
    • 编译器优化(如函数内联)可能影响其内容。
(2) .data 段(数据段)
  • 内容 :存放 已初始化全局变量静态变量 (包括全局/局部静态变量)。
  • 特点
    • 变量在编译时已明确初始值,这些值直接保存在目标文件中。
    • 例如: int global_var = 42; 或函数内的 static int s_var = 10;
(3) .bss 段(Block Started by Symbol)
  • 内容 :存放 未初始化全局变量静态变量 (或初始化为 0 的变量)。
  • 特点
    • 不占用目标文件的物理存储空间 (仅记录变量的大小和符号)。
    • 程序加载时由操作系统分配内存并初始化为零(节省磁盘空间)。
    • 例如: int uninit_var;static int s_uninit;
(4) .rodata 段(只读数据段)
  • 内容 :存放 只读数据 ,如字符串常量、 const 修饰的全局变量等。
  • 特点
    • 程序运行时不可修改,尝试写入会触发段错误(Segmentation Fault)。
    • 例如: const char* str = "Hello"; 中的 "Hello" 会存放在此段。

2. 内存布局中的其他区域

虽然严格来说不属于目标文件的段,但程序运行时内存中的关键区域:

(1) 堆(Heap)
  • 动态分配的内存区域 (如 malloc / new 申请的内存)。
  • 由程序员手动管理(分配和释放),空间通常从低地址向高地址增长。
(2) 栈(Stack)
  • 存放局部变量、函数参数、返回地址 等。
  • 由编译器自动管理,空间从高地址向低地址增长。
  • 函数调用时压栈(push),返回时弹栈(pop)。

3. 其他辅助段

(1) .comment
  • 存放编译器版本、作者等注释信息,不参与程序运行。
(2) .debug
  • 调试信息(如符号表、行号映射),用于 GDB 等调试工具。
(3) .plt & .got (动态链接相关)
  • .plt (过程链接表)和 .got (全局偏移表)用于动态链接库的延迟绑定(Lazy Binding)。

4. 关键区别与设计思想

段名是否占用磁盘空间初始化方式典型内容
.text✔️编译时确定函数代码
.data✔️编译时写入初始值已初始化的全局变量
.bss运行时由操作系统初始化为零未初始化的全局变量
.rodata✔️编译时确定,运行时不可修改字符串常量、 const 变量
为什么需要 .bss 段?
  • 节省磁盘空间 :未初始化的变量无需在文件中存储零值。
  • 提升加载效率 :程序启动时操作系统批量清零 .bss 段,比读取大量零值更高效。

5. 实例验证

以下 C 代码的变量存储位置:

int global_init = 10;         // .data
int global_uninit;            // .bss
static int static_init = 20;  // .data
static int static_uninit;     // .bss
const int global_const = 30;  // .rodata

int main() {
    static int local_static_init = 40;   // .data
    static int local_static_uninit;      // .bss
    char* str = "Hello";                 // str在栈,字符串在.rodata
    int local_var;                       // 栈
    return 0;
}

总结

书中通过分析目标文件的结构(如 ELF 格式),解释了这些段如何被链接器组织、操作系统加载,最终形成进程的内存映像。理解这些段的划分,对优化程序体积、分析内存泄漏、调试底层问题至关重要。