第二章:C/C++内存管理
1、 五大分区核心定义与特性

2、代码实例分区定位分析

答案:C C C A A A A A D A B
解析:
1. globalVar —— C 数据段(已初始化全局变量)
2. staticGlobalVar —— C 数据段(全局静态变量)
3. staticVar —— C 数据段(局部静态变量,存静态区,不进栈)
4. localVar —— A 栈(普通局部变量,函数栈帧)
5. num1 —— A 栈(局部数组,数组整体开辟在栈上)
6. char2 —— A 栈(字符数组,数组本体在栈,初始化时把常量区"abcd"拷贝至栈空间)
7. *char2 —— A 栈(数组首元素,位于栈内存)
8. pChar3 —— A 栈(指针变量本身是局部变量,存在栈上)
9. *pChar3 —— D 代码段(指针指向字符串字面量,字面量存放于只读常量区)
10. ptr1 —— A 栈(指针变量本体在栈)
11. *ptr1 —— B 堆(指针指向malloc开辟的堆内存空间)
核心易错点:
1. char char2[] = "abcd";
数组名char2存放在栈区,程序执行时,会将常量区的字符串字面量"abcd\0",拷贝一份到栈上数组空间,因此可以修改char2[0]='x',不会报错。
2. const char* pChar3 = "abcd";
指针变量pChar3本体存放在栈区,指针仅保存常量区字符串"abcd\0"的地址,字符串本体在只读代码段,尝试修改*pChar3='x',程序直接触发段错误。
3. int* ptr1 = malloc();
指针ptr1本体存放在栈区,malloc向操作系统申请堆内存,指针存储堆空间起始地址,*ptr1访问堆内存,堆内存必须手动free释放。

3、C语言中动态内存管理方式:malloc/calloc/realloc/free
void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3);
}
这里不清晰的同学可以回顾一下之前的内容:
4、C++动态内存管理:new/delete
4.1 new/delete操作内置类型

语法规范
单个对象:new 严格匹配 delete
连续数组空间:new[] 严格匹配 delete[]
交叉混用会产生未定义行为,大概率造成内存泄漏,程序崩溃。
4.2 new和delete操作自定义类型
malloc/free:只分配一块原始堆内存,不会调用构造、析构函数,对象无法完成合法初始化与资源清理;
new/delete:分配内存 + 自动调用构造函数初始化对象;释放前自动调用析构函数清理成员堆资源。
4.2.1 代码对比演示:
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】
// 除了开空间还会调用构造函数和析构函数
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A) * 10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}
4.3 operator new与operator delete函数
new、delete是运算符,并非底层内存分配函数,其底层会自动调用系统全局函数:operator new、operator delete,两个底层函数本质是对malloc/free的封装。
4.3.1 operator new 底层逻辑
1. 内部调用malloc申请指定字节堆内存;
2. 内存申请成功,直接返回堆内存起始指针;
3. 内存申请失败,不会返回NULL,默认抛出bad_alloc异常,需要开发者使用try-catch捕获。
4.3.2 operator delete 底层逻辑
内部直接封装free函数,仅负责把传入的堆内存归还操作系统,无额外逻辑。
4.3.3 new运算符执行完整两步流程
1. 调用operator new函数申请空间
4.3.4 delete运算符执行完整两步流程
4.4 new[] 与 delete[]:动态创建对象数组
当需要在堆上批量创建多个同类对象时,使用new[],释放必须严格配套delete[]。
4.4.1 执行原理
1. new[]:调用operator new[]批量申请一整片连续堆内存,循环N次调用构造函数,逐个初始化数组内每一个对象;
2. delete[]:循环N次调用析构函数,逐个销毁数组内对象、清理资源,最后调用operator delete[]释放整片连续堆内存。
4.4.2 底层隐藏内存布局
示例代码:
int main()
{
// 堆上创建10个A对象,自动执行10次构造函数
A* arr = new A[10];
// 必须使用delete[],执行10次析构函数,再释放整片数组内存
delete[] arr;
return 0;
}
编译器执行new A[10]时,会在数组首地址向前预留一块隐藏4字节空间,专门存储数组元素个数(本例存储10)。
delete[]会读取该隐藏数值,确定析构函数调用次数;若释放时漏写[],无法读取元素个数,只会调用1次析构函数,剩余9个对象资源泄漏,严重时程序直接宕机。
5、malloc/free和new/delete的区别
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)