在C/C++的世界里,内存从来不是“取之不尽、用之不竭”的抽象概念——它是程序运行的“物理舞台”,每一个变量的诞生、每一次函数的调用、每一段数据的流转,都依赖于对这片舞台的精准调度。与那些自带“内存管家”(如垃圾回收机制)的编程语言不同,C/C++将内存的控制权彻底交还给程序员:你可以亲手分配一块内存,决定它的生命周期,也能在不需要时主动释放它。这种“绝对掌控”既是C/C++高效性的根源,也是无数bug的温床。 内存管理的细节往往决定了代码的健壮性与可靠性。 本文将从内存的分配机制入手,拆解堆与栈的本质区别,剖析`malloc/free`与`new/delete`的底层逻辑,更会直面内存管理中最棘手的问题——如何识别泄漏、规避野指针、处理动态数组的边界风险。无论你是刚接触C/C++的新手,还是希望填补知识漏洞的开发者,掌握这些内容,不仅能让你的代码远离隐蔽的内存陷阱,更能理解程序运行的底层逻辑,真正驾驭C/C++赋予的“极致控制力”。

一C/C++中内存区域划分

C/C++中,内存空间分为堆区,栈区,静态区,常量区,按操作系统层面来说,静态区又叫数据段,代码区又叫代码段。

【说明】
1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
4. 数据段--存储全局数据和静态数据。
5. 代码段--可执行的代码/只读常量。

不同的区域存放的数据是不一样的,看下面这个问题:

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

1)malloc

2)realloc

3)calloc

这些我们都很熟悉,只看一个思考题

#include<iostream>

using namespace std;

void test01()
{
	int* p1 = (int*)calloc(10, sizeof(int));
	int* p2 = (int*)realloc(p1, sizeof(int));
	free(p2);
	//还要free p1吗?
}

int main()
{
	test01();
	return 0;
}

解析:realloc函数在调整内存大小时,若成功,会将原内存块(p1 指向的)的管理权限转移到新的内存块(p2 指向的),此时 p1 已经失效。只需释放 p2 即可,若再 free p1 会导致重复释放内存的错误。

4)malloc的实现原理

glibc中malloc实现原理s

三C++内存管理方式

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

1)对内置类型操作

void test02()
{
	int* p1 = new int;
	//动态申请一个int类型的空间

	int* p2 = new int[10];
	//动态申请十个int类型的空间,一个数组

	//初始化
	int* p3 = new int(1);
	//动态申请一个int类型的空间,初始化为1

	int* p4 = new int[10] {1};
	//动态申请十个int类型的空间,初始化为1

	//内存释放
	delete p1;//单个对象
	delete[] p2;//数组
	delete p3;
	delete p4;
}

2)对自定义类型操作

在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数。

多对象申请空间会多次调用构造函数。

申请多个对象会保存数据个数,来决定调用析构函数的次数(如果有资源需要释放的话)

void test03()
{
	//在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数
	A* a2 = (A*)malloc(sizeof(A));
	free(a2);
	A* a1 = new A;
	delete a1;

	int* b1 = (int*)malloc(sizeof(int));
	free(b1);
	int* b2 = new int;
	delete b2;

	A* a3 = (A*)malloc(sizeof(A));
	free(a3);
	A* a4 = new A[sizeof(A) * 10];
	delete []a4;
}

int main()
{
	test03();
	return 0;
}

四operator new和operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operatornew和operatordelete是系统提供的全局函数,new在底层调用operatornew全局函数来申请空间,delete在底层通过operator  delete全局函数来释放空间。

我们只要知道,operator new实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operatordelete最终是通过free来释放空间的。

五new和delete的实现原理

1)内置类型

Lnew/delete和malloc/free基本没区别,申请失败new会抛异常,malloc会返回NUL。

2)自定义类型

new的原理
1.调用operator new函数申请空间
2.在申请的空间上执行构造函数,完成对象的构造

delete的原理
1.在空间上执行析构函数,完成对象中资源的清理工作
2.调用operator delete函数释放对象的空间
newT[N]的原理
1.调用operator new[]函数,在operator ne[]中实际调用operator new函数完成N个对象空间的申请
2.在申请的空间上执行N次构造函数

delete[]的原理
1.在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2.调用operator delete[释放空间,实际在operator delete[中调用operator delete来释放空间

六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在释放空间前会调用析构函数完成空间中资源的清理释放

七定位new(了解)

在 C++ 中,定位 new(placement new) 是一种特殊的 new 操作符形式,它允许在已分配的内存块上构造对象,而不重新申请新的内存。其核心作用是 “利用已有内存空间创建对象”,主要用于内存池、缓存管理等需要精确控制对象构造位置的场景。

#include <new>  // 必须包含此头文件

// 在指针 p 指向的内存地址上构造一个 T 类型的对象
new (p) T(构造参数);

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐