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";
	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);
}

由上代码做下边的题:
1.选择题:
选项: A.栈 B.堆 C.数据段 D.代码段
globalVar在哪里? C
staticGlobalVar在哪里? C
staticVar在哪里? C
localVar在哪里? A
num1 在哪里? A
char2在哪里? A
*char2在哪里? A
pChar3在哪里? == A==
*pChar3在哪里? D
ptr1在哪里? A
*ptr1在哪里?B
2.填空题
sizeof(num1) 40
sizeof(char2) 5—>要带\0
strlen(char2) 4
sizeof(pChar3) 4或者8 32位平台是4,64位平台是8
strlen(pChar3) 4
sizeof(ptr1)4或者8 2位平台是4,64位平台是8

经过上面题的检验,让我们进行总结,如下图
C/C++内存管理_析构函数
【说明】

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

(1)malloc/calloc/realloc/和free

先让我们回顾一下他们分别是干什么的,让我们打开MADN

面试题:malloc/calloc/realloc的区别

C/C++内存管理_动态内存_02
1.开辟一个新的空间,参数是字节数,返回类型是void*
2.malloc与free同时出现,二者必须要在一起
C/C++内存管理_构造函数_03
1.如果当前的空间不够,进行扩大,在原来的基础上。
2.将原有的数据从头到尾拷贝到新分配的内存区域。
3.第一个参数:原来的空间;第二个参数:新开辟的空间。
C/C++内存管理_初始化_04
1.功能:在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向起始地址的指针;如果分配不成功,返回NULL。
2.跟malloc的区别:calloc在动态分配完内存后,自动初始化该空间为0,而malloc里边的数据都是随机数。
3.和malloc的相同点:和free同时出现。

C++内存管理方式

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

(1)new/delete操作内置类型

void Test()
{
	//动态申请了一个int型的空间
	int* ptr4 = new int;

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

	//动态申请了3个int类型的空间
	int* ptr6 = new int[3];

	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}

C/C++内存管理_析构函数_05
【注意】
1.申请和释放单个元素的空间,使用new和delete操作符
2.申请和释放连续的空间,使用new[]和delete[]

new和delete操作自定义类型

class Test
{
public:
	Test()
		:_data(0)
	{
		cout << "Test():" << endl;
	}
	~Test()
	{
		cout << "~Test():" << endl;
	}

private:
	int _data;
};

void Test1()
{
	//申请单个Test类型的空间
	Test* p1 = (Test*)malloc(sizeof(Test));
	free(p1);

	//申请10个Test类型的空间
	Test* p2 = (Test*)malloc(sizeof(Test)*10);
	free(p2);
}

void Test2()
{
	//申请单个Test类型的空间
	
	Test* p1 = new int;//先开空间,后初始化
	delete(p1);

	//申请10个Test类型的空间
	Test* p2 = new int[10];//先调用析构函数,在delete
	delete[] p2;
}

new和delete的实现原理

(1)内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和 释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL。

(2)自定义类型

new的原理
1.调用operator new函数申请空间
2.在申请的空间上执行构造函数。完成对象的构造
delete的原理
1.在空间上执行析构函数,在进行资源的清理
20调用operator delete函数释放对象的空间
==new T[N]的原理 ==

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数 2. 在申请的空间上执行N次构造函数
    ==delete[]的原理 ==
  3. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  4. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
常见面试题

1.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在释放空间前会调用析构函数完成空间中资源的清理
  7. new/delete比malloc和free的效率稍微低点,因为new/delete的底层封装了malloc/free

2.内存泄漏

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上 的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

void MemoryLeaks()
{
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;

	//异常安全问题
	int* p3 = new int[10];

	Func();//这里Func函数抛异常导致 delete[] p3没有执行,p3没被释放

	delete[] p3;
}