第二章:C/C++内存管理

**1、**五大分区核心定义与特性

在Linux操作系统下,C/C++程序运行时,用户态内存会被操作系统划分为5个固定区域,从上至下依次为:内核空间、栈、内存映射段、堆、数据段、代码段(常量区)。
内核空间:用户程序无权读写访问,由操作系统内核专属使用,用于管理进程调度、硬件驱动、系统调用**。**

  1. **栈:**向下增长,由编译器自动分配、自动释放,生命周期跟随函数栈帧。存放局部非静态变量、函数形参、函数返回地址、数组局部变量。
  2. **内存映射段:**是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享共享内存,做进程间通信。
  3. **堆:**向上增长,用于动态内存分配。
  4. **数据段(静态区):**存储全局变量和静态变量。
  5. **代码段(常量区):**存放可执行的代码、只读常量、字符串字面量。

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",拷贝一份到栈上数组空间,因此可以修改char20='x',不会报错。

  1. const char* pChar3 = "abcd";

指针变量pChar3本体存放在栈区,指针仅保存常量区字符串"abcd\0"的地址,字符串本体在只读代码段,尝试修改*pChar3='x',程序直接触发段错误。

  1. int* ptr1 = malloc();

指针ptr1本体存放在栈区,malloc向操作系统申请堆内存,指针存储堆空间起始地址,*ptr1访问堆内存,堆内存必须手动free释放。

3、C 语言中动态内存管理方式: malloc/calloc/realloc/free

cpp 复制代码
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);
}

这里不清晰的同学可以回顾一下之前的内容:

动态内存管理(1)-CSDN博客

4、C++动态内存 管理:new/delete

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因
此C++又提出了自己的内存管理方式:通过 new delete 操作符进行动态内存管理

4.1 new/delete 操作内置类型


语法规范

单个对象:new 严格匹配 delete

连续数组空间:new\[\] 严格匹配 delete\[\]

交叉混用会产生未定义行为,大概率造成内存泄漏,程序崩溃。

4.2 newdelete****操作自定义类型

malloc/free:只分配一块原始堆内存,不会调用构造、析构函数,对象无法完成合法初始化与资源清理;
new/delete:分配内存 + 自动调用构造函数初始化对象;释放前自动调用析构函数清理成员堆资源。

4.2.1 代码对比演示:

cpp 复制代码
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 newoperator 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函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造初始化
    反汇编如下:

4.3.4 delete运算符执行完整两步流程

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间
    反汇编如下:

4.4 new\[\] 与 delete\[\]:动态创建对象数组

当需要在堆上批量创建多个同类对象时,使用new\[\],释放必须严格配套delete\[\]。

4.4.1 执行原理

  1. new\[\]:调用operator new\[\]批量申请一整片连续堆内存,循环N次调用构造函数,逐个初始化数组内每一个对象;

  2. delete\[\]:循环N次调用析构函数,逐个销毁数组内对象、清理资源,最后调用operator delete\[\]释放整片连续堆内存。

4.4.2 底层隐藏内存布局

示例代码:

cpp 复制代码
int main()
{
    // 堆上创建10个A对象,自动执行10次构造函数
    A* arr = new A[10];
    // 必须使用delete[],执行10次析构函数,再释放整片数组内存
    delete[] arr;
    return 0;
}

编译器执行new A10时,会在数组首地址向前预留一块隐藏4字节空间,专门存储数组元素个数(本例存储10)。

delete\[\]会读取该隐藏数值,确定析构函数调用次数;若释放时漏写\[\],无法读取元素个数,只会调用1次析构函数,剩余9个对象资源泄漏,严重时程序直接宕机。

5、malloc/free new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地
方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,
    如果是多个对象,\[\]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需
    要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new
    在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成
    空间中资源的清理释放
相关推荐
小龙报5 小时前
【优选算法】1.搜索插入位置 2.x的平方根
java·c语言·数据结构·c++·python·算法·蓝桥杯
A_humble_scholar5 小时前
Linux(二) C++ TCP/UDP网络编程
linux·网络·c++
SNAKEpc121386 小时前
OpenGL(十五)- 着色器语言GLSL
c语言·c++·线性代数·算法·矩阵·图形渲染·着色器
欧特克_Glodon6 小时前
OpenCV计算机视觉开发入门与实践<六>:OpenCV架构模块简介
c++·人工智能·opencv·计算机视觉
Rabitebla7 小时前
C++11 新特性详解(一):列表初始化、initializer_list 与右值引用
java·开发语言·数据结构·c++·算法·leetcode·list
MC皮蛋侠客7 小时前
TDengine C++ 系列(11):性能模型与调优——从测量到优化
c++·php·tdengine
兔兔兔兔18 小时前
记录C++ 3
开发语言·c++
代码地平线8 小时前
C++类与对象(中):默认成员函数与日期类实战
c++·笔记·算法
hansang_IR8 小时前
【题解】可持久化区间仿射区间和(Persistent Range Affine Range Sum)
c++·算法·线段树