C/C++内存管理,malloc,realloc,calloc,new,delete详解!!!

1.初步了解内存中各个区间存储的数据特征

1.栈区:存储一些局部变量、函数参数、返回值等,跟函数栈振有关,出了作用域,生命周期结束。

2.堆区:用于动态开辟空间,如果不主动销毁空间,则程序运行结束,生命周期结束。

3.数据段(静态区):static修饰的静态变量和全局变量,程序运行结束,生命周期结束。

4.代码段(常量区):可执行的代码和常量。

练习

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* pChar3 = "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);
}
  选项: A.   B.   C.数据段(静态区)  D.代码段(常量区)   globalVar在哪里?__1__   staticGlobalVar在哪里?__2__   staticVar在哪里?__3__   localVar在哪里?__4__   num1 在哪里?__5__   char2在哪里?__6__   *char2在哪里?__7_   pChar3在哪里?__8__       *pChar3在哪里?__9__   ptr1在哪里?__10__         *ptr1在哪里?__11__
1.C       2.C       3.C        4. A       5.A        6.A        7. A       8.A        9.D        10.A        11.B
结合上图可以得知,cha2其本身存放在栈区,指针指向栈区中数组首元素的地址,再将静态区中”abcd”赋值给数组,所以*char指向的元素在栈区!!! pChar3其本身存放在栈区,指向静态区中存放”abcd”字符串的首地址!!!

2.c语言实现内存管理

2.1malloc

在堆上开一快符合你预期大小的一块空间,并且返回指向该地址空间的指针

void* malloc (size_t size);

size:开多大的空间,单位是字节

2.2realloc

如果malloc开辟出来的空间开少了,realloc可以在堆上重新开一块符合你预期大小的空间,并返回指向该空间的指针

void* realloc (void * ptr,size_t size);

ptr:初始空间的地址

size:将空间开辟到多大

 2.3calloc

用于对开辟的空间进行初始化,calloc会将开辟的空间中每个元素初始化为0

void* realloc (size_t num,size_t size);

num:分配的元素数量

size:每个元素的大小

3.c++实现内存管理

3.1 new/delete new []/delete []

在c++中,有两对操作符new/delete、new []/delete [],他们的作用是负责开空间和释放空间!!!

new作用跟malloc类似,delete作用跟free类似

new和delete在面对自定义类型时会去调用构造函数和析构函数

new =先申请对象空间 再调用构造函数

delete = 先调用析构函数 再释放对象空间

3.1.1内置类型

当需要开一个整形的空间时。

int main()
{ 
	//malloc
	int* p1 = (int*)malloc(sizeof(int));
	//new开空间用法跟malloc不同,但是作用相同,都是负责开空间!!!
	int* p2 = new int;

	//free
	free(p1);
	//delete
	delete(p2);
	return 0;
}

当需要开多个空间时 例如开10个int类型的空间 需要加上[]

int main()
{
	int* p1 = (int*)malloc(sizeof(int)*10);
	int* p2 = new int[10];

	free(p1);
	delete[](p2);
	return 0;
}
3.1.2自定义类型

new和delete在面对自定义类型时会去调用构造函数和析构函数

new =先开空间 再调用构造函数

delete = 先调用析构函数 再释放空间

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = new int[capacity];
		int _top = 0;
		int _capacity = capacity;
	}
	~Stack()
	{
		delete (_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack* p2 = new Stack;
	//new先开空间 再去调用构造函数
	delete(p2);
	//delete先调用析构函数,再去释放空间
	return 0;
}

如果需要开10个Stack类型的空间 用new[] 和 delete[]

int main()
{
	Stack* p2 = new Stack[10];
	//new先开空间 再去调用构造函数
	delete[](p2);
	//delete先调用析构函数,再去释放空间
	return 0;
}

 3.2new/delete底层实现原理

3.2.1 全局函数 operator new/operator delete

要知道原理,我们就得先知道operatot new / operator delete 这两个全局函数

在c++中,有这样两个全局函数,他们的作用跟malloc、free类似,都是负责开空间和释放空间!!!

注意:operator new / operator delete 不是new/delete的重载,而是两个全局函数!!!

我们知道malloc申请空间失败的时候,会返回空。而operator new申请空间失败的时候则会抛异常。我们可以理解为operator是封装的malloc。

也就是说operator new 和mclloc除了申请空间失败的处理方法不同,其他的用法以及功能是相同的!!!

operator delete 可以理解为跟operator new对应,其用法和功能跟free完全一样!!!

int main()
{
	Stack* p1 = (Stack*)malloc(sizeof(Stack));
	free(p1);
	 
	//operator new底层用的是malloc
	Stack* p2 = (Stack*)operator new (sizeof(Stack));
	//operator delete底层用是free
	operator delete(p2);
	return 0;
}
3.2.2 new/delete 和 operator new/operator delete的关系

new = 1.申请对象空间 + 2.调用构造

delete = 1.调用析构 + 2.释放对象空间

在底层原理上,new的第一步申请对象空间底层就是调用operator new函数,

operator的第二步释放对象空间底层就是调用operator operator函数.

也就是说下面两段不同的代码,起到的作用都是相同的 

int main()
{
	Stack* p1 = new Stack;
	delete (p1);
	
    //等价于new
	Stack* p2 = (Stack*)operator new (sizeof(Stack));
	//定位new显示调用构造函数
	new(p2)Stack;
    //等价于delete
	p2->~Stack();
	operator delete(p2);
	return 0;
}

我们到汇编语言的角度证明一下

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年12月5日
下一篇 2023年12月5日

相关推荐