C++内存模型演示:代码、原理与注意事项
本文通过一段带详细注释的C++代码,结合内存区域划分(栈、堆、全局/静态区、常量区、代码区),直观解释C++的内存模型。代码运行后会打印各变量的内存地址,通过地址分布可清晰区分不同内存区域,同时附带核心原理说明与关键注意事项。
一、C++内存模型演示代码
#include <iostream>
#include <cstdlib> // 用于 malloc/free
using namespace std;
// 1. 全局变量:存放在【全局/静态存储区】(程序启动时分配,结束时释放)
int global_var = 10;
// 2. 静态全局变量:同样存放在【全局/静态存储区】,仅当前文件可见
static int static_global_var = 20;
// 3. 全局常量:存放在【常量存储区】(只读,不可修改)
const int const_global_var = 30;
// 函数:存放在【代码区】(只读,存放指令)
void test_memory(int param){
// param:函数参数,存放在【栈】
// 4. 局部变量:存放在【栈】(自动分配,函数执行完自动释放)
int local_var = 40;
// 5. 静态局部变量:存放在【全局/静态存储区】(仅初始化一次,生命周期贯穿程序)
static int static_local_var = 50;
// 6. 局部常量:存放在【栈】(C++11后可能优化到常量区,但本质是局部作用域)
const int const_local_var = 60;
cout << "=== 函数内部变量(test_memory)===" << endl;
cout << "函数参数(栈): param = " << param << ", 地址 = " << ¶m << endl;
cout << "局部变量(栈): local_var = " << local_var << ", 地址 = " << &local_var << endl;
cout << "静态局部变量(全局/静态区): static_local_var = " << static_local_var << ", 地址 = " << &static_local_var << endl;
cout << "局部常量(栈): const_local_var = " << const_local_var << ", 地址 = " << &const_local_var << endl;
// 7. 堆内存分配:通过 new 手动分配,存放在【堆】(需手动释放,否则内存泄漏)
int* heap_var1 = new int(70); // 堆上分配单个 int
int* heap_var2 = new int[3]{80, 90, 100}; // 堆上分配数组
cout << "\n=== 堆内存(手动分配)===" << endl;
cout << "堆变量 1(new int): *heap_var1 = " << *heap_var1 << ", 地址 = " << heap_var1 << endl;
cout << "堆数组(new int[]): heap_var2[0] = " << heap_var2[0] << ", 地址 = " << heap_var2 << endl;
// 堆内存释放:必须手动 delete,否则程序结束前不会释放(内存泄漏)
delete heap_var1; // 释放单个堆变量
delete[] heap_var2; // 释放堆数组(注意加 [])
heap_var1 = nullptr; // 避免野指针
heap_var2 = nullptr;
}
int main(){
cout << "=== 全局/静态存储区 & 常量区 ===" << endl;
cout << "全局变量:global_var = " << global_var << ", 地址 = " << &global_var << endl;
cout << "静态全局变量:static_global_var = " << static_global_var << ", 地址 = " << &static_global_var << endl;
cout << "全局常量:const_global_var = " << const_global_var << ", 地址 = " << &const_global_var << endl;
// 调用函数,演示栈、堆
test_memory(100); // 实参 100 会被拷贝到形参 param(栈上)
// 演示栈溢出(可选,取消注释运行会崩溃)
// void stack_overflow() { stack_overflow(); } // 递归过深,栈空间耗尽
// stack_overflow();
return 0;
}
二、代码编译与运行
以下为 Linux/macOS环境下的编译运行命令,Windows环境(MinGW)编译命令相同,运行时替换为 memory_demo.exe 即可。
# 编译
g++ memory_demo.cpp -o memory_demo
# 运行
./memory_demo
三、典型运行输出
说明:以下地址仅为示例,不同系统、编译器运行结果略有差异,但各内存区域的地址分布规律一致。
=== 全局/静态存储区 & 常量区 ===
全局变量:global_var = 10, 地址 = 0x564b8e7a2044
静态全局变量:static_global_var = 20, 地址 = 0x564b8e7a2048
全局常量:const_global_var = 30, 地址 = 0x564b8e7a1f04
=== 函数内部变量(test_memory)===
函数参数(栈): param = 100, 地址 = 0x7ffd2b3c4aac
局部变量(栈): local_var = 40, 地址 = 0x7ffd2b3c4aa8
静态局部变量(全局/静态区): static_local_var = 50, 地址 = 0x564b8e7a204c
局部常量(栈): const_local_var = 60, 地址 = 0x7ffd2b3c4aa4
=== 堆内存(手动分配)===
堆变量 1(new int): *heap_var1 = 70, 地址 = 0x564b8e9f7eb0
堆数组(new int[]): heap_var2[0] = 80, 地址 = 0x564b8e9f7ed0
=== 常量存储区 ===
字符串常量(常量区): str_const = Hello, C++ Memory!, 地址 = 0x564b8e7a1f28
四、核心内存区域原理解释
通过代码输出的地址分布和变量特性,可明确 C++ 的 5 大内存区域,各区域核心信息如下表所示:
| 内存区域 | 存放内容 | 分配方式 | 生命周期 | 核心特点 |
|---|---|---|---|---|
| 代码区(Code) | 函数指令、编译后的机器码 | 编译器自动分配 | 程序启动到结束 | 只读(防止指令被篡改),地址通常最低廉 |
| 常量区(RO Data) | 字符串常量、const 全局变量 | 编译器自动分配 | 程序启动到结束 | 只读(修改会崩溃),地址与全局/静态区接近但独立 |
| 全局/静态区(Data/BSS) | 全局变量、static 变量(全局/局部) | 编译器自动分配 | 程序启动到结束 | 静态分配,仅初始化一次(static 局部变量不会随函数调用重新初始化) |
| 栈(Stack) | 局部变量、函数参数、返回值 | 编译器自动分配 | 作用域内有效(函数执行完释放) | 空间小(通常几 MB),分配效率极高(栈指针移动),向下生长 |
| 堆(Heap) | new/malloc 分配的动态内存 | 程序员手动分配 | 手动释放(delete/free) | 空间大(可到 GB 级),分配效率低(需查找空闲块),向上生长,容易内存泄漏 |
五、关键注意事项(代码中隐含的坑)
- 堆内存泄漏:若忘记 delete(如注释掉 delete heap_var1),堆内存会一直占用,直到程序结束。长期运行的程序(如服务器)会因内存泄漏崩溃。
- 野指针:delete 后未将指针置为 nullptr,指针仍指向已释放的堆地址,后续访问会导致程序崩溃(未定义行为)。
- 栈溢出:递归过深、局部变量过多(如超大数组 int arr[1000000])会耗尽栈空间,触发栈溢出崩溃。
- 常量区只读:字符串常量('Hello…')存放在常量区,试图修改(如 str_const[0] = 'h')会导致运行时崩溃。
- static 局部变量:仅初始化一次,下次函数调用时保留上次的值(代码中 static_local_var 每次调用都是 50,不会重新赋值)。
六、总结
通过这段代码,可直观理解 C++ 内存的分配机制和各区域特性,这也是面试中高频考点(如堆与栈的区别、内存泄漏原因等)。实际开发中,合理管理堆内存、避免栈溢出、区分常量与变量的存储区域,是写出高效稳定代码的关键。

