一、代码区(Code Segment)

代码区存储程序的二进制代码,包括函数和程序逻辑的可执行指令。该区域通常是只读的,以防止程序运行过程中意外修改代码内容,同时也可能存储一些只读的常量,例如字符串字面量。程序运行时,代码区被加载到内存中,并作为程序逻辑的执行基础。例如,以下代码的 main 函数逻辑会存储在代码区:

1
2
3
4
5
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl; // 字符串 "Hello, World!" 也存储在代码区
return 0;
}

二、全局/静态存储区(Global/Static Storage)

全局/静态存储区用于存储全局变量和静态变量,这些变量的生命周期与程序一致,从程序启动到结束始终存在于内存中。全局变量在程序的任何地方都可以访问,而静态变量的作用域通常局限于其声明的范围内,但其值在多次调用间保持不变。以下代码展示了全局变量和静态变量的存储行为:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
int globalVar = 10; // 全局变量,存储在全局/静态存储区
void func() {
static int staticVar = 0; // 静态变量,存储在全局/静态存储区
staticVar++;
std::cout << staticVar << std::endl;
}
int main() {
func(); // 输出 1
func(); // 输出 2
return 0;
}

在上述代码中,全局变量 globalVar 和静态变量 staticVar 都存储在全局/静态存储区。

三、栈区(Stack)

栈区用于存储函数调用时的局部变量函数参数以及返回地址。栈的内存分配和释放由编译器自动完成,管理效率高。栈内存是以后进先出的方式管理的,当函数调用结束后,栈上分配的内存会被自动回收。例如:

1
2
3
4
5
6
7
8
9
#include <iostream>
void func(int a, int b) {
int localVar = a + b; // 局部变量,存储在栈区
std::cout << localVar << std::endl;
}
int main() {
func(3, 4); // 输出 7
return 0;
}

在上述代码中,ablocalVar 都是局部变量,它们在函数 func 的栈帧中分配内存。当 func 调用结束后,这些内存会被回收。

四、堆区(Heap)

堆区用于动态分配内存(C++ 的 new 或 C 的 malloc 等),分配的内存大小由程序员决定,并且需要手动释放。堆区的内存灵活性较高,但如果未正确释放内存,会导致内存泄漏问题。例如:

1
2
3
4
5
6
7
#include <iostream>
int main() {
int* dynamicArray = new int[10]; // 动态分配内存,存储在堆区
dynamicArray[0] = 42; // 使用动态内存
delete[] dynamicArray; // 手动释放内存
return 0;
}

在上述代码中,使用 new 分配的动态数组位于堆区,需要通过 delete[] 释放内存。如果忘记释放内存,将导致内存泄漏。

五、常量区(Constant Storage)

常量区用于存储程序中的常量数据,例如字符串字面量和其他编译时常量。常量区通常是只读的,试图修改常量的行为可能会导致未定义行为。例如:

1
2
3
4
5
6
#include <iostream>
int main() {
const char* str = "Hello, Constant!"; // 字符串字面量存储在常量区
std::cout << str << std::endl;
return 0;
}

在上述代码中,字符串 "Hello, Constant!" 被存储在常量区,而指针 str 本身存储在栈区。

六、总结

C/C++ 的内存分区包括代码区全局/静态存储区栈区堆区常量区,各个区域的职责分工明确,确保程序运行的高效和稳定。

  1. 代码区存储程序的二进制代码,提供程序逻辑的基础;
  2. 全局/静态存储区管理全局和静态变量,生命周期贯穿程序始终;
  3. 栈区存储局部变量和函数调用信息,分配和释放效率高;
  4. 堆区用于动态内存分配,但需要程序员手动管理;
  5. 常量区用于存储只读的常量数据。

最后给出一张我做的示意图,以帮助加深记忆和理解。

图 6.1 内存分区模型示意图