C++内存分区
代码区存放程序代码,数据区存全局变量,堆区动态分配内存,栈区存函数局部变量。
C++内存分区
C++内存分区
进程地址空间是指进程能够访问的虚拟地址范围。在大多数操作系统中(如 Linux、Windows 等),进程地址空间被划分为两个主要部分:
- 用户态(用户区)
- 内核态(内核区)
以 32 位 Linux 为例(3G/1G 分布):
区域 | 地址范围 | 所属空间 | 说明 |
---|---|---|---|
用户区 | 0x00000000 ~ 0xBFFFFFFF | 用户态 | 上面图里的部分 |
内核区 | 0xC0000000 ~ 0xFFFFFFFF | 内核态 | 所有进程共享,进程无法访问 |
内核区是操作系统在每个进程虚拟地址空间中保留的一块地址范围,用来存放内核代码、数据、驱动程序和关键内核结构(如进程控制块PCB)。这部分地址对所有进程共享映射,但普通用户态进程无法访问,只有进入内核态时才能操作,保证系统安全和资源管理统一。
以下只讨论用户区。
内存结构图示意
+-------------------------+
| 栈 Stack | <--- 高地址 |
| ------------------------- |
| 空间 (可能是库) |
| ------------------------- |
| 堆 Heap | <--- malloc/new分配从低地址往高地址扩展 |
| ------------------------- |
| BSS(未初始化全局变量) |
| ------------------------- |
| Data(已初始化全局/静态) |
| ------------------------- |
| Text(代码区) | <--- 低地址 |
+-------------------------+
这个结构描述的是 一个 C/C++ 程序在进程中的典型虚拟内存布局(即进程地址空间结构)。这是一种逻辑视图,不是物理内存,而是操作系统为每个进程划分的虚拟地址空间。
1. 代码区(Text Segment)
内容:程序的机器指令(即编译后的可执行代码)。
特点:
通常是只读的,防止程序意外修改指令。
多个相同程序的进程之间可以共享这块区域(节省内存)。
2. 全局/静态区(Data Segment)
分为两部分:
已初始化全局变量和静态变量区(Initialized Data Segment)
内容:被初始化的全局变量和静态变量。
1 2
int a = 10; // 已初始化全局变量 static int b = 20; // 已初始化静态变量
未初始化全局变量和静态变量区(BSS Segment)
内容:未显式初始化的全局变量和静态变量(系统自动初始化为 0)。
1 2
int a; // 未初始化全局变量,位于 BSS 段 static int b; // 未初始化静态变量,位于 BSS 段
3. 栈区(Stack Segment)
内容:函数调用过程中的局部变量、函数参数、返回地址、保存的寄存器等。
特点:
自动分配和释放,由编译器管理。
遵循先进后出(FILO)的原则。
大小受限,容易造成栈溢出(如递归过深)。
1
2
3
void func() {
int x = 10; // 局部变量,位于栈上
}
4. 堆区(Heap Segment)
- 内容:动态分配的内存(程序运行时用
new
或malloc
分配的)。 - 特点:
- 手动管理(需要使用
free
或delete
释放)。 - 大小较大,但容易造成内存泄漏。
- 分配和释放效率相对较低,碎片化风险高。
- 手动管理(需要使用
1
2
int* p = new int[100]; // 动态申请的内存,在堆上
delete[] p; // 释放
5. 常量区(Literal or Constant Segment)
- 内容:程序中的常量,比如字符串常量、
const
修饰的全局变量(有时实现上会被放入代码段)。
1
2
const int c = 100; // 有些实现中在常量区
char* str = "hello"; // 字符串常量位于常量区
举个例子总结
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
int global_a = 10; // 已初始化全局变量 → Data Segment
int global_b; // 未初始化全局变量 → BSS Segment
const int global_c = 30; // 常量 → 可能在常量区
int main() {
int local_var = 5; // 局部变量 → 栈区
static int static_var = 20; // 静态变量 → Data Segment
int* heap_var = new int(100); // 动态分配 → 堆区
cout << *heap_var << endl;
delete heap_var; // 手动释放堆内存
return 0;
}
各分区的实现细节依赖操作系统和编译器,例如:
- Windows 和 Linux 的地址布局可能不同;
const
局部变量通常还是在栈上;const char*
指向的字符串常量位于常量区,但变量本身在栈上。
更精细的程序内存结构图(以 Linux 为例)
+---------------------------+
| 栈(stack) | <-- 高地址 |
| --------------------------- |
| 共享库映射区 |
| --------------------------- |
| 堆(heap) |
| --------------------------- |
| .bss(未初始化全局变量) |
| --------------------------- |
| .data(已初始化的全局变量) |
| --------------------------- |
| .rodata(只读常量) | ← 常量就在这里 |
| --------------------------- |
| .text(代码段) | <-- 低地址 |
+---------------------------+
之前的内存结构示意图为何没有常量区:
图中没画常量区,是因为“常量”不是操作系统层面一个独立的内存段;
常量通常在
.rodata
(只读数据段),属于数据段一部分;在教学中,为了方便理解会说“常量区”,但实际它们会被链接到
.rodata
并映射为只读内存页面。
本文由作者按照 CC BY 4.0 进行授权