内存管理是 C/C++ 程序设计中至关重要的一环,它不仅关系到程序的性能,还直接影响程序的稳定性和安全性。本文将系统性地介绍 C/C++ 中的内存分布、动态内存管理方式及其底层原理,帮助你构建清晰的内存管理知识体系。
目录
[一、C/C++ 程序内存布局](#一、C/C++ 程序内存布局)
[二、C 语言动态内存管理:malloc / calloc / realloc / free](#二、C 语言动态内存管理:malloc / calloc / realloc / free)
[三、C++ 内存管理方式:new / delete](#三、C++ 内存管理方式:new / delete)
[与 malloc/free 的最大区别](#与 malloc/free 的最大区别)
[四、operator new 与 operator delete](#四、operator new 与 operator delete)
[自定义 operator new/delete](#自定义 operator new/delete)
[五、new 和 delete 的实现原理](#五、new 和 delete 的实现原理)
[5.1 内置类型](#5.1 内置类型)
[5.2 自定义类型](#5.2 自定义类型)
[六、定位 new 表达式(placement new)](#六、定位 new 表达式(placement new))
[七、malloc/free 与 new/delete 的区别总结](#七、malloc/free 与 new/delete 的区别总结)
一、C/C++ 程序内存布局
在 C/C++ 程序中,内存通常被划分为以下几个区域:
| 区域 | 存储内容 |
|---|---|
| 栈(Stack) | 局部变量、函数参数、返回值等,由编译器自动分配和释放,向下增长。 |
| 堆(Heap) | 动态分配的内存,由程序员手动管理(malloc/free、new/delete),向上增长。 |
| 数据段(Data Segment) | 全局变量、静态变量(static)。 |
| 代码段(Code Segment) | 可执行代码、字符串常量等只读数据。 |
| 内存映射段(Memory Mapping 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}; // 栈
char char2[] = "abcd"; // 栈(数组在栈上,字符串内容在栈中复制)
const char* pchar3 = "abcd"; // pchar3在栈,"abcd"在代码段
int* ptr1 = (int*)malloc(sizeof(int) * 4); // ptr1在栈,指向堆内存
}
二、C 语言动态内存管理:malloc / calloc / realloc / free
C 语言提供了以下动态内存分配函数:
| 函数 | 说明 |
|---|---|
malloc |
分配指定字节数的未初始化内存。 |
calloc |
分配并清零内存,适用于数组。 |
realloc |
调整已分配内存的大小,可能移动内存块。 |
free |
释放已分配的内存。 |
常见面试题
-
malloc / calloc / realloc 的区别?
-
malloc只分配不初始化; -
calloc分配并初始化为 0; -
realloc用于调整内存大小。
-
-
malloc 的实现原理?
- 在 glibc 中,
malloc通过维护一个内存块链表来管理堆内存,使用首次适应、最佳适应等算法分配内存。
- 在 glibc 中,
三、C++ 内存管理方式:new / delete
C++ 引入了 new 和 delete 操作符,它们在 C 语言的基础上增加了对对象生命周期的管理。
基本使用
cpp
// 单个对象
int* p1 = new int;
int* p2 = new int(10); // 初始化为 10
delete p1;
delete p2;
// 对象数组
int* p3 = new int[10];
delete[] p3;
与 malloc/free 的最大区别
对于自定义类型 ,new 会调用构造函数,delete 会调用析构函数,而 malloc/free 不会。
cpp
class A {
public:
A() { cout << "构造" << endl; }
~A() { cout << "析构" << endl; }
};
A* p1 = (A*)malloc(sizeof(A)); // 不会调用构造函数
A* p2 = new A; // 调用构造函数
free(p1); // 不会调用析构函数
delete p2; // 调用析构函数
四、operator new 与 operator delete
new 和 delete 在底层调用的是全局函数 operator new 和 operator delete:
-
operator new内部调用malloc,失败时抛出std::bad_alloc异常; -
operator delete内部调用free。
自定义 operator new/delete
我们可以重载这两个函数来实现自定义内存管理策略,例如内存池。
五、new 和 delete 的实现原理
5.1 内置类型
与 malloc/free 类似,区别在于:
-
new失败时抛异常,malloc返回NULL; -
new[]/delete[]用于连续空间。
5.2 自定义类型
-
new:
-
调用
operator new分配内存; -
调用构造函数初始化对象。
-
-
delete:
-
调用析构函数清理资源;
-
调用
operator delete释放内存。
-
六、定位 new 表达式(placement new)
定位 new 用于在已分配的内存上构造对象,常用于内存池场景。
cpp
A* p = (A*)malloc(sizeof(A));
new(p) A(); // 在 p 指向的内存上调用构造函数
p->~A(); // 显式调用析构函数
free(p);
七、malloc/free 与 new/delete 的区别总结
| 特性 | malloc/free | new/delete |
|---|---|---|
| 语言 | C 函数 | C++ 操作符 |
| 初始化 | 不初始化 | 可初始化 |
| 大小计算 | 手动计算 | 自动计算(类型) |
| 返回值 | void*(需强转) | 类型指针(无需强转) |
| 失败处理 | 返回 NULL | 抛出异常 |
| 对象生命周期管理 | 不调用构造/析构函数 | 调用构造/析构函数 |
结语
理解 C/C++ 内存管理是写出高效、稳定程序的基础。从内存布局到动态分配,从 malloc/free 到 new/delete,再到底层的 operator new/delete 和定位 new,每一个环节都值得深入学习和实践。希望本文能帮助你构建清晰的内存管理知识框架。
本文基于《C/C++ 内存管理》学习笔记整理而成,适用于面试准备和系统学习。建议结合实际代码练习,加深理解。