C++ 内存空间
在 C++ 中,程序运行时使用的内存主要分为几个不同的区域,其中最重要的是栈(Stack)和堆(Heap)。
内存空间的主要区域
-
代码区(Text Segment)
- 存储程序的机器指令(编译后的代码)
- 通常是只读的
-
全局/静态区(Data Segment)
- 存储全局变量和静态变量
- 分为:
- 已初始化数据区(
.data
) - 未初始化数据区(
.bss
)
- 已初始化数据区(
-
栈(Stack)
- 自动管理的内存区域
- 存储局部变量、函数参数、返回地址等
-
堆(Heap)
- 动态分配的内存区域
- 需要手动管理(或通过智能指针管理)
栈(Stack) vs 堆(Heap) 详细对比
特性 | 栈(Stack) | 堆(Heap) |
---|---|---|
分配方式 | 自动分配/释放 | 手动分配/释放 (new /delete ) |
管理方式 | 编译器自动管理 | 程序员手动管理 |
分配速度 | 非常快(只需移动栈指针) | 相对较慢(需要查找合适内存块) |
内存大小 | 较小(通常几MB) | 较大(受系统可用内存限制) |
生命周期 | 函数执行期间 | 直到显式释放 |
碎片问题 | 无 | 可能有(频繁分配释放后) |
访问速度 | 更快(CPU缓存友好) | 稍慢 |
扩展方向 | 通常向下增长 | 通常向上增长 |
典型用途 | 局部变量、函数调用 | 动态大小或生命周期长的对象 |
代码示例
#include <iostream>
// 全局变量 - 存储在全局/静态区
int global_var = 10;
void stackExample() {
// 局部变量 - 存储在栈上
int stack_var = 20;
std::cout << "Stack variable: " << stack_var << std::endl;
// 函数结束时自动释放
}
void heapExample() {
// 动态分配 - 存储在堆上
int* heap_var = new int(30);
std::cout << "Heap variable: " << *heap_var << std::endl;
// 必须手动释放,否则内存泄漏
delete heap_var;
}
int main() {
stackExample();
heapExample();
// 静态局部变量 - 存储在全局/静态区
static int static_var = 40;
std::cout << "Static variable: " << static_var << std::endl;
return 0;
}
使用建议
-
优先使用栈内存:
- 对于生命周期与函数相同的小型对象
- 访问更快,自动管理更安全
-
需要使用堆内存的情况:
- 对象需要比创建它的函数更长的生命周期
- 对象太大(可能超过栈大小限制)
- 需要动态大小的数据结构(如运行时才知道大小的数组)
-
现代C++最佳实践:
- 尽量使用智能指针(
std::unique_ptr
,std::shared_ptr
)而不是裸指针管理堆内存 - 对于容器类,优先使用标准库容器(
std::vector
,std::string
等),它们内部会合理管理内存
- 尽量使用智能指针(
-
避免常见错误:
- 栈溢出(递归太深或分配大数组)
- 内存泄漏(忘记释放堆内存)
- 悬垂指针(访问已释放的内存)
- 双重释放(多次释放同一内存)
动态分配内存
/*
* 动态分配内存
*/
//1. 声明一个指针,用new 运算符向系统申请一块内存,让指针指向这块内存
int* p = new int(5);
std::cout<<"p: "<<p<<" *p: "<<*p<<std::endl;
//2. 通过指针解引用方法,像使用变量一样使用这块内存
*p = 8;
std::cout<<"p: "<<p<<" *p: "<<*p<<std::endl;
//3. 如果这块内存不用了,用delete释放这块内存
delete p;
return 0;
// 注意:如果动态分配的内存不用了,必须delete释放,否则可能用尽系统的内存。
//结果
p: 0x12aa0f70 *p: 5
p: 0x12aa0f70 *p: 8