1. C/C++ 程序内存区域划分
程序在运行时,操作系统会将其内存划分为不同的区域,每个区域有特定的用途。
- 内核空间 (Kernel Space): 用户代码不能读写,受保护的系统区域。
- 栈 (Stack): 向下增长。存储非静态局部变量、函数参数、返回值等。
- 内存映射段 (Memory Mapping Segment): 用于文件映射、动态库加载、匿名映射。是高效的I/O映射方式。
- 堆 (Heap): 向上增长。用于程序运行时动态内存分配 (malloc/new)。
- 数据段 (Data Segment): 存储全局数据和静态数据 (static variables)。
- 代码段 (Code Segment): 存储可执行代码和只读常量 (如字符串常量)。
cpp
int globalVar = 1; // 数据段
static int staticGlobalVar = 1; // 数据段
void Test() {
static int staticVar = 1; // 数据段
int localVar = 1; // 栈
int num1[10] = {1, 2, 3, 4}; // num1数组本身在栈
char char2[] = "abcd"; // char2数组在栈,内容从常量区拷贝过来
const char* pChar3 = "abcd"; // pChar3指针在栈,指向代码段(常量区)
int* ptr1 = (int*)malloc(4); // ptr1指针在栈,指向堆
free(ptr1);
}
2. C语言动态内存管理
C语言主要通过四个函数进行动态内存管理。
malloc(size_t size): 分配指定字节大小的内存,不初始化(内容为随机值)。calloc(size_t num, size_t size): 分配内存并将内容初始化为 0。realloc(void* ptr, size_t size): 调整已分配内存块的大小。free(void* ptr): 释放内存。
3. C++ 动态内存管理 (new / delete)
C++ 引入了 `new` 和 `delete` 操作符,不仅能分配内存,还能自动处理对象的构造和析构。
3.1 使用方法
- 单个对象:
new Type(申请),delete ptr(释放)。 - 数组:
new Type[N](申请),delete[] ptr(释放)。 - 初始化:
new int(10)(初始化为10)。
3.2 自定义类型 (核心优势)
对于自定义类型 (class/struct):
new: 先开辟空间,然后自动调用构造函数。delete: 先自动调用析构函数,然后释放空间。malloc/free: 只负责开辟/释放空间,不调用构造和析构函数。
cpp
class A {
public:
A(int a = 0) : _a(a) { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
private:
int _a;
};
int main() {
A* p1 = new A(1); // 分配内存 + 调用构造函数
delete p1; // 调用析构函数 + 释放内存
A* p2 = (A*)malloc(sizeof(A)); // 仅分配内存
free(p2); // 仅释放内存
}
4. operator new 与 operator delete
new 和 delete 是操作符,而 operator new 和 operator delete 是系统提供的全局函数。
4.1 实现机制
- operator new: 底层通过循环调用
malloc申请空间。如果失败,尝试执行空间不足应对措施;如果没有措施,抛出bad_alloc异常。 - operator delete: 底层通过调用
free释放空间。
注意:我们一般不直接调用这两个函数,而是使用
new关键字,编译器会自动生成调用它们的代码。
4.2 重载 (了解)
可以重载这两个函数来实现特殊的内存管理需求(如内存池、内存泄漏检测日志)。
5. new 和 delete 的底层实现原理
5.1 内置类型
对于 int, char 等内置类型,new/delete 与 malloc/free 基本一致。主要区别在于 new 失败会抛异常,而 malloc 返回 NULL。
5.2 自定义类型 (T)
- new T: 调用
operator new-> 调用构造函数。 - delete p: 调用析构函数 -> 调用
operator delete。 - new T[N]: 调用
operator new[](其内部调用operator new) -> 执行 N 次构造函数。 - delete[] p: 执行 N 次析构函数 -> 调用
operator delete[](其内部调用operator delete)。
6. 定位 new 表达式 (Placement-new)
定位 new 允许我们在已分配的原始内存空间 中调用构造函数初始化一个对象。通常配合内存池使用。
语法
new (place_address) Type(initializer-list);
使用步骤
- 分配内存 (如使用 malloc 或 内存池)。
- 使用定位 new 显示调用构造函数。
- 显示调用析构函数 (显式写法:
p->~A())。 - 释放内存 (如 free)。
cpp
// 模拟:p1 是一块已经分配好的内存
A* p1 = (A*)malloc(sizeof(A));
// 在 p1 指向的内存上构造对象 A
new(p1) A(10);
// 必须显式调用析构函数
p1->~A();
// 最后释放物理内存
free(p1);