C 语言内存布局

17 Mar 2025 | 4 分钟阅读

当我们创建一个 C 程序并运行它时,它的可执行文件会以一种有组织的方式存储在计算机的 RAM 中。

C 程序的内存布局如下图所示

Memory Layout in C

从上图我们可以观察到,C 程序在程序中包含以下几个部分

  • 文本段
  • 已初始化数据段
  • 未初始化数据段
  • Stack

让我们逐一了解每个部分。

1. 文本段

文本段也称为代码段。当我们编译任何程序时,它会创建一个像 a.out、.exe 等的可执行文件,该文件存储在 RAM 内存的文本或代码段中。如果我们将在硬盘上存储指令,那么从硬盘访问指令的速度会变慢,因为硬盘工作在串行通信模式,所以从硬盘获取数据会变慢,而 RAM 直接连接到数据总线和地址总线,因此从 RAM 访问数据速度更快。

2. 数据段

我们在程序中使用的数据将存储在数据段中。由于在 main() 函数内部声明的变量存储在堆栈中,但在 main() 方法外部声明的变量将存储在数据段中。在数据段中声明的变量可以以已初始化、未初始化、局部或全局的形式存储。因此,数据段分为四类,即已初始化、未初始化、全局或局部。

让我们通过一个例子来理解这种情况。

在上面的代码中,var1 和 var2 变量在 main() 函数外部声明,其中 var1 是未初始化变量,而 var2 是已初始化变量。这些变量可以在程序中的任何位置访问,因为这些变量不是堆栈中 main() 的一部分。

数据段包含两个段

  • 未初始化数据段
  • 已初始化数据段

未初始化数据段

未初始化数据段也称为 **.bss** 段,它存储所有未初始化的全局、局部和外部变量。如果全局、静态和外部变量未被初始化,它们默认会被赋予零值。

.bss 段代表 **Block Started by symbol (符号开始的块)**。bss 段包含存储所有静态分配变量的目标文件。这里的静态分配对象是指那些没有显式初始化的对象,它们会被初始化为零值。在上面的代码中,var1 是一个未初始化变量,因此它存储在未初始化数据段中。

让我们看一些未初始化数据段的例子。

已初始化数据段

已初始化数据段也称为数据段。数据段是程序的虚拟地址空间,它包含所有由程序员显式初始化的全局和静态变量。

数据段中的变量值不是只读的,也就是说,它们可以在运行时修改。此数据段可以进一步分为几类:

  • **已初始化只读区域:** 这是变量值不能被修改的区域。
  • **已初始化读写区域:** 这是变量值也可以被更改的区域。

例如:像 **char str[] = "javatpoint"** 和 **int a=45** 这样的全局变量将存储在已初始化的读写区域。如果我们创建全局变量,如 const char* string = "javatpoint";,则字面量 "javatpoint" 将存储在已初始化的只读区域,而 char 指针变量将存储在已初始化的写入区域。

3. 堆栈

当我们定义一个函数并调用该函数时,我们会使用堆栈帧。在函数内部声明的变量存储在堆栈中。函数参数也存储在函数中,因为参数也是函数的一部分。这种类型的内存分配称为静态内存分配,因为所有变量都在函数中定义,并且变量的大小也在编译时定义。堆栈段在内存中起着非常重要的作用,因为每当调用函数时,都会创建一个新的堆栈帧。

堆栈也用于递归函数。当函数在同一个函数中反复调用自身时,会导致堆栈溢出条件,并导致程序中出现段错误。

4. 堆

堆内存用于动态内存分配。堆内存从未初始化数据段的末尾开始,并向上增长到更高的地址。malloc() 和 calloc() 函数用于在堆中分配内存。堆内存可供所有共享库和动态加载的模块使用。free() 函数用于释放堆中的内存。