关注

C/C++内存管理

1、C/C++内存分布

在这里插入图片描述

  1. 栈(Stack)(堆栈):存非静态局部变量/函数参数/返回值等 等,栈是向下增长的。
  2. 堆(Heap) :用于程序运行时动态内存分配,堆是可以上增长的。
  3. 数据段:存储全局数据和静态数据。
  4. 代码段:可执行的代码/只读常量。

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

需包含头文件 stdlib.h
malloc:在堆上申请空间(不初始化)。
返回值:
成功:返回 void 类型(通常需强转)空间的起始地址;
失败:返回空指针。

calloc:在堆上申请空间(初始化)。
返回值:与malloc相同。

realloc:对已申请的空间扩容(malloc/calloc)。
分原地扩容(已申请的空间后空间够)和异地扩容(已申请的空间后空间不够,将数据拷贝到新开的空间,释放原空间)。对空指针使用行为和malloc相同。
返回值:
成功:返回 void 类型的空间的起始地址。
失败:返回空指针,原数据仍在。

free:释放堆上申请的空间(需起始地址)。
只能对堆上已申请的空间释放,一次申请释放一次,对 NULL/nullptr 释放则不执行。无返回值

#include<stdlib.h>

int main()
{
	//申请五个整型的空间
	int* i = (int*)malloc(5 * sizeof(int));

	//申请五个整型的空间并初始化
	int ci = (int*)calloc(sizeof(int), 5);

	//对i扩容为10个整型的空间
	int* ri = (int*)realloc(i, 10 * sizeof(int));

	//空间释放
	free(ri);
	free(ci);

	return 0;
}

2、C++内存管理

在c++,在原本c的基础上引入了一套新的内存管理机制new/delete
在这里插入图片描述

可以粗略的认为:
new = malloc + 调用构造函数
delete = 调用析构函数 + free
注意:c++的机制并不能完全代替c的机制。


3、operator new与operator delete函数

operator new和operator delete是系统提供的全局函数,当调用new和delete,其实是分别在底层调用operator new和operator delete。
operator new返回失败要默认是抛出bad_alloc的异常,但我们可以自定义执行什么操作。

//捕获bad_alloc异常 (x86下)
int main()
{
	int n = 0;
	try {
		while (1)
		{
			//每次申请1M
			char* i = new char[1024 * 1024];
			cout << n << endl;
			n++;
		}
	}
	catch (const exception& e) {
		//打印bad_alloc
		cout << e.what() << endl;
	}

}

在这里插入图片描述


4、new和delete的实现原理

4、1 内置类型

new 与 delete 的行为和 malloc 与 free 的行为类似,在底层 new 调用 malloc ,delete 调用 free 。但new 申请一个空间,new [] 申请连续的空间,失败抛异常; delete 释放一个空间,delete[] 释放连续的空间。

4、2 自定义类型

new的原理
1、调用 operator new 函数,申请一个对象的空间。
2、调用一次 T 的构造函数。
new T[n]的原理
1、调用 operator new[] ,申请n个对象的空间。
2、调用n次 T 的构造函数。
delete的原理
1、调用一次 T 的析构函数。
2、 operator delete 函数,释放一个对象的空间。
delete[] 的原理
1、调用n次 T 的析构函数。
2、调用 operator delete[] 函数,释放n个对象的空间。

4、3 new和delete不匹配

要点:对类类型new[]开空间,会在返回地址前多开一个整型大小的空间做计数器,delete[]会获取计数器存的数值,调用对应次的析构。

//隐式默认成员函数
class A {
private:
	int _a;
};

//显式默认成员函数
class B {
public:
	B(int b = 1)
		:_b(b)
	{
		cout << "B(int b)" << endl;
	}

	~B()
	{
		cout << "~B()" << endl;
	}
private:
	int _b;
};

int main()
{
	//内置类型

	//正常
	//int* a1 = new int(1);
	//int* a2 = new int(1);

	//delete[] a1;
	//delete[] a2;

	//正常
	//int* b1 = new int[5];
	//int* b2 = new int[5];

	//delete b1;
    //delete b2;

	//--------------------------------------------------
	//自定义类型
	// 
	//正常
	//A* b1 = new A;
	//A* b2 = new A;

	//delete[] b1;
	//delete[] b2;

	//死循环(析构出错)
	//析构b1时 b1空间前的垃圾值被delete[]获取,导致计数器存的值是垃圾值。

	//B* b1 = new B;
	//B* b2 = new B;

	//delete[] b1;
	//delete[] b2;

	//正常
	//A* b1 = new A[5];
	//A* b2 = new A[5];

	//delete b1;
	//delete b2;

	//报错(不在起始地址释放空间)
	//b1前的计数器存申请空间的个数,看似开5个整型的空间,实则空间前多开一个整型大小的计数器,
	//b1指向计数器之后的地址,delete时,不在申请空间的起始地址释放而报错。

	//B* b1 = new B[5];
	//B* b2 = new B[5];

	//delete b1;
	//delete b2;

	return 0;
}

5、定位new表达式(placement-new)

定位new格式
new(指针)构造函数
指针:指向已申请好的空间
可以粗略的认为 operator new 和 定位 new 实现new的开空间和构造的分离;调用析构和operator delete 实现delete的析构和释放的分离。operator new是构造内存池的核心。
内存池:为了提效,一次性申请大量空间,减少频繁申请。

class A {
public:
	A(int a)
		:_a(a)
	{ 
		cout << "A(int a)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

int main()
{

	//只开空间
	A* a = (A*)operator new(sizeof(A));

	//只初始化
	new(a)A(1);

	//析构
	a->~A();

	//只释放空间
	operator delete(a);

	return 0;
}

6、malloc/free和new/delete的区别总结

本质
malloc/free 是函数,new/delete 是操作数。
申请的空间
malloc 申请不初始化,new 可以初始化。对自定义类型,new还会调用构造。
malloc既可以申请单个空间,又可以申请连续的空间;new只能申请单个空间,申请连续的空间需用new[]。
malloc申请空间的大小需计算,new不需要。
返回值
malloc返回的是 void 类型的指针,通常需要强转,new返回的是指定的类型。
申请空间失败
malloc返回空指针,new抛异常。
空间释放
对自定类型delete会先调用他的析构,再释放;free直接释放。

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2602_95531348/article/details/159693921

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--