0
点赞
收藏
分享

微信扫一扫

坐牢第三十八天(Qt)

hoohack 2024-09-16 阅读 19
c++

目录

1. C/C++内存分布 

 2. C语言中动态内存管理

3. C++内存管理方式

3.1 new/delete操作内置类型 

3.2 new和delete操作自定义类型 

4. operator new和operator delete函数 

4.1 operator new和operator delete函数 

5. new和delete的实现原理 

5.1 内置类型

5.2 自定义类型

6. 定位new表达式(placement-new)

7. malloc/free和new/delete的区别


1. C/C++内存分布 

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* pchar = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}
/*
globalvar在:数据段(静态区)
staticGlobalvar在:数据段(静态区)
staticvar在:数据段(静态区) - Test函数结束以后staticvar变量不销毁
localvar在:栈
num1在:栈

char2在:栈 - char2和num1一样都是数组,数组名表示首元素的地址,
存的是地址,在栈上

*char2在:栈 - char2在进行运算的时候代表整个数组,整个栈上的数组

pchar3在:栈 - 局部的指针,也是在栈上。
*pchar3在:代码段(常量区) - pchar3指向的是首元素的地址,
解引用得到首元素,而这个'a'就放在代码段(常量区)

ptr1在:栈 - 局部的指针,在栈上
*ptr1在:堆 - 解引用指向动态开辟的空间,在堆上
*/

 2. C语言中动态内存管理

1. malloc/calloc/realloc的区别是什么?

malloc适用于分配未初始化的内存块,calloc适用于分配并初始化的内存块,realloc适用于调整已有内存块的大小。

2. malloc的实现原理。

【CTF】GLibc堆利用入门-机制介绍_哔哩哔哩_bilibiliCTF Pwn中堆相关机制的入门讲解。内容比较浅,讲得也比较快,如果发现什么错误还望指正。, 视频播放量 6376、弹幕量 12、点赞数 170、投硬币枚数 151、收藏人数 331、转发人数 41, 视频作者 bili53448916889, 作者简介 开学了,大概会更得慢一点?,相关视频:【CTF】GLibc堆利用-Double Free,【CTF Pwn】GLibc堆利用-Off-By-One,【CTF】CTF题型简介,【网络安全】CTF夺旗赛新手入门到进阶实操,含历届真题详解!,PWN入门教程-工具讲解、栈溢出漏洞简单利用 网络安全CTF竞赛 信息安全管理与评估 网络与信息安全管理员,B站最好的绿盟科技CTF夺旗赛教程 从小白入门到比赛实战,2023羊城杯pwn_01(risky_login),【暗网黑客教程】审核下架34次,终于上传成功,你敢学我就敢发,学不会我来教~(暗網怎么进/暗網教程/如何上暗網/手機如何上暗網),【全网最全讲解】1分钟带你了解什么是CTF?,【CTF-杂项】task_图片隐写subicon-default.png?t=O83Ahttps://www.bilibili.com/video/BV117411w7o2/?spm_id_from=333.788.videocard.0

3. C++内存管理方式

3.1 new/delete操作内置类型 

int main()
{
	//动态申请一个int类型的空间
	int* ptr4 = new int;
	//动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);

	//动态申请10个int类型的空间
	int* ptr6 = new int[3];
	//动态申请10个int类型的空间并初始化 - 跟数组的初始化是一样的
	int* ptr7 = new int[3]{ 1,2,3 };
	int* ptr8 = new int[5]{ 1,2,3 };

	//释放
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
	delete[] ptr7;
	delete[] ptr8;

	return 0;
}

3.2 new和delete操作自定义类型 

#include <iostream>
using namespace std;
class A
{
public:
	A(int a = 1)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	//new/delete 和 malloc/free最大区别是 new/deeletc对于
	//自定义类型除了开空间还会调用构造函数和析构函数。
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A;//没有默认构造就会报错,当然也可以自己传参
	//A* p3 = new A(1);//这里有点像匿名对象,但不是匿名对象
	//这是在堆上new一个对象,然后构造。然后把对象的地址返回给p3

	free(p1);//释放空间
	delete p2;//析构+释放空间 - 释放空间之前先调析构函数

	//创建多个对象
	A* p4 = (A*)malloc(sizeof(A) * 10);
	A* p5 = new A[10];//new了10个A对象,那么就会调用10次构造函数
	free(p4);
	delete[] p5;//析构的时候会对这10个对象依次析构,然后释放内存空间

	//创建多个对象并且初始化
	//A aa1(1);
	//A aa2(2);
	//A* p6 = new A[10]{aa1,aa2};//调用有名对象,后面的调默认构
	//造,没默认构造就报错

	//也可以直接用匿名对象
	//A* p6 = new A[10]{ A(1),A(2) };
	//也可以用隐式类型转换
	A* p6 = new A[10]{ 1,2 };//本质是构造临时对象,然后走拷贝构造,
	//编译器优化成了直接构造也是一样的
	delete[] p6;

	return 0;
}

前两个传参,后面直接调用默认构造。 

 没有默认构造的话代码就会出问题,所以一个类最好提供默认构造,可以减少不必要的麻烦。

例子:构造一个链表

struct ListNode
{
	int _index;
	ListNode* _next;
	ListNode(int index = 1)
		:_index(index)
		,_next(nullptr)
	{}
};
int main()
{
	//new相当于开空间+调用ListNode的构造函数初始化
	ListNode* n1 = new ListNode(1);
	ListNode* n2 = new ListNode(2);
	ListNode* n3 = new ListNode(3);
	ListNode* n4 = new ListNode(4);
	n1->_next = n2;
	n2->_next = n3;
	n3->_next = n4;
	//释放
	delete n1;
	delete n2;
	delete n3;
	delete n4;
	return 0;
}

4. operator new和operator delete函数 

4.1 operator new和operator delete函数 

/*
operator new:该函数实际通过malloc来申请空间,
当malloc申请空间成功时直接返回,申请空间失败,
尝试执行空间不足应对措施,如果该应对措施用户设置了,
则继续申请,否则抛异常。
operator new本质是对malloc的一个封装
封装是因为malloc失败会返回0,而operaotr new失败之后会抛异常
异常是C++处理错误的一种方式
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
	void* p;
	while ((p = malloc(size)) == 0)
		if (_callnewh(size) == 0)
		{
			// report no memory
			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}
/*
operator delete:该函数最终是通过free来释放空间的
operator delete封装的不是free,而是封装的一个_free_dbg
的一个函数,其实_free_dbg就是free,其实库里面的free是一个
宏函数,所以我们也可以理解为operator delete就是对free的封装。
*/
void operator delete(void* pUserData)
{
	_CrtMemBlockHeader* pHead;
	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
	if (pUserData == NULL)
		return;
	_mlock(_HEAP_LOCK);  /* block other threads */
	__TRY
		/* get a pointer to memory block header */
		pHead = pHdr(pUserData);
	/* verify block type */
	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK);  /* release other threads */
	__END_TRY_FINALLY
		return;
}
/*
free的实现 - 底层也是调_free_dbg
*/
#define   free(p)	_free_dbg(p, _NORMAL_BLOCK)

5. new和delete的实现原理 

5.1 内置类型

5.2 自定义类型

C++中的抛异常: 

#include <iostream>
using namespace std;
void Func()
{
	int* p1 = new int[1024 * 1024 * 100];//400MB
	cout << p1 << endl;
	int* p2 = new int[1024 * 1024 * 100];
	cout << p2 << endl;
	int* p3 = new int[1024 * 1024 * 100];
	cout << p3 << endl;
	int* p4 = new int[1024 * 1024 * 100];
	cout << p4 << endl;
	int* p5 = new int[1024 * 1024 * 100];
	cout << p5 << endl;
	//当申请p5的时候就会失败,因为没那么大内存了
	//弹出的框就是抛异常了。
}
int main()
{
	Func();
	return 0;
}

那么我们就可以使用try catch来捕获异常。 

#include <iostream>
using namespace std;
void Func()
{
	int* p1 = new int[1024 * 1024 * 100];//400MB
	cout << p1 << endl;
	int* p2 = new int[1024 * 1024 * 100];
	cout << p2 << endl;
	int* p3 = new int[1024 * 1024 * 100];
	cout << p3 << endl;
	int* p4 = new int[1024 * 1024 * 100];
	cout << p4 << endl;
	int* p5 = new int[1024 * 1024 * 100];
	cout << p5 << endl;
	//当申请p5的时候就会失败,因为没那么大内存了
	//弹出的框就是抛异常了。
}
int main()
{
	try
	{
		Func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

operator new我们是可以直接使用的 

class A
{ };
//operator new我们是可以直接使用的
int main()
{
	//跟malloc区别是失败会抛异常,所以不用检查返回值
	//但是没有调用构造函数
	//new也一样,不用检查返回值
	A* p1 = (A*)operator new(sizeof(A));
	return 0;
}

6. 定位new表达式(placement-new)

#include <iostream>
using namespace std;
class A
{
public:
	A(int a = 1)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	//构造函数是没办法显示调用的
	//也就是说new没办法拆分为operator new和调拷贝构造
	A* ptr1 = (A*)operator new(sizeof(A));
	//ptr1->A(1);//err

	//相对这块显示调用构造函数的空间的用法叫做定位new
	new(ptr1)A(101);//这个时候就会对p1指向的空间调用构造函数初始化

	//析构函数支持显示调用
	ptr1->~A();
	operator delete(ptr1);
	//上半部分就充当了new的功能,下半部分就充当
	//了detete的功能,和new和delete达到的效果一摸一样
	return 0;
}
/*
但是平时不会这样写,这个定位new和显示调用析构
有些地方为了提高效率申请内存的时候如果直接用new
的话底层就是调operator new,也就是说调malloc了,
这种方式效率比较低,那么就会写一个叫内存池的东西
那如果我的空间是从我的内存池出来的,那就开好了空间
初始化的话就要用定位new去初始化,析构就是显示调用
析构,然后释放内存池,
*/

7. malloc/free和new/delete的区别

举报

相关推荐

0 条评论