目录
[2、 C语言中动态内存管理方式:malloc/calloc/realloc/free](#2、 C语言中动态内存管理方式:malloc/calloc/realloc/free)
[3.1 new/delete操作内置类型](#3.1 new/delete操作内置类型)
[3.2 new和delete操作自定义类型](#3.2 new和delete操作自定义类型)
[4.operator new与operator delete函数](#4.operator new与operator delete函数)
[5. new和delete的实现原理](#5. new和delete的实现原理)
[5.1 内置类型](#5.1 内置类型)
[5.2 自定义类型](#5.2 自定义类型)
1、1.C/C++内存分布
我们先来看下面的一段代码和相关问题
cpp
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);
}
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__
分析:
globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区
staticVar静态局部变量在静态区 localVar局部变量在栈区
num1局部变量在栈区
char2在哪里?__A__ *char2在哪里?__A__
pChar3在哪里?__A__ *pChar3在哪里?__D__
ptr1在哪里?__A__ *ptr1在哪里?__B__
分析:
char2局部变量在栈区
char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上
pChar3局部变量在栈区 *pChar3得到的是字符串常量字符在代码段
ptr1局部变量在栈区 *ptr1得到的是动态申请空间的数据
【说明】
1、栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的,栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放,频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题。
2、内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(现在只需要了解, 后面在linux中会提到)
3、堆用于程序运行时动态内存分配,堆是可以上增长的。堆无法静态分配,只能动态分配
4、数据段--存储全局数据和静态数据。
5、代码段--可执行的代码/只读常量。
2、 C语言中动态内存管理方式:malloc/calloc/realloc/free
cpp
void Test()
{
int* p1 = (int*)malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof(int));
int* p3 = (int*)realloc(p2, sizeof(int) * 10);
// 这里需要free(p2)吗?
free(p3);
}
- malloc/calloc/realloc的区别?
三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。
(1)malloc函数。其原型void *malloc(unsigned int num_bytes);
num_byte为要申请的空间大小,需要我们手动的去计算,如int *p = (int*)
**malloc( 20*sizeof(int)),**如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请一个80Byte的连续空间,并将空间基地址强制转换为int类型,赋值给指针p,此时申请的内存值是不确定的。
(2)calloc函数,其原型void *calloc(size_t n, size_t size);
其比malloc函数多一个参数,并不需要人为的计算空间的大小,比如如果他要申请20个int类型空间,会int *p = (int *)calloc(20, sizeof(int)),这样就省去了人为空间计算的麻烦。但这并不是他们之间最重要的区别,malloc申请后空间的值是随机的,并没有进行初始化,而calloc却在申请后,对空间逐一进行初始化,并设置值为0;
既然calloc不需要计算空间并且可以直接初始化内存避免错误,那为什么不直接使用calloc函数,那要malloc要什么用呢?
实际上,任何事物都有两面性,有好的一面,必然存在不好的地方。这就是效率。calloc函数由于给每一个空间都要初始化值,那必然效率较malloc要低,并且现实世界,很多情况的空间申请是不需要初始值的,这也就是为什么许多初学者更多的接触malloc函数的原因。
(3)realloc函数和上面两个有本质的区别,其原型void realloc(void *ptr, size_t new_Size)
用于对动态内存进行扩容(及已申请的动态空间不够使用,需要进行空间扩容操作,ptr为指向原来空间基址的指针, new_size为接下来需要扩充容量的大小。
3、C++内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
3.1 new/delete操作内置类型
cpp
void Test()
{
//内置类型
// 管理对象
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
//管理对象数组
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
//动态申请10个int类型的空间,并初始化为1 2 3 4
int* ptr7 = new int[10] {1, 2, 3, 4}; //后面的会默认初始化为0
//释放一个对象
delete ptr4;
delete ptr5;
//释放多个对象
delete[] ptr6;
delete[] ptr7;
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。
3.2 new和delete操作自定义类型
new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
cpp
class A
{
public:
A(int a = 0)
: _a(a)
{
//cout << "A():" << this << endl;
cout << "A():" << endl;
}
A(int a1, int a2)
{
cout << "A(int a1, int a2)" << endl;
}
A(const A& aa)
: _a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A()
{
//cout << "~A()" << this << endl;
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大区别是
// new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(2);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A) * 10);
A* p6 = new A[10]; //调用10次构造函数
free(p5);
delete[] p6; //调用10次析造函数
//多个对象
A aa1(1);
A aa2(2);
A aa3(3);
A* p7 = new A[10]{ aa1,aa2,aa3 };
delete[]p7;
// 多参数
A* p7 = new A[10]{ 1,2,3,4,5,{6,7}};//用1生成临时对象,然后被编译器合二为一(隐式对象转换)
delete[]p7;
return 0;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会
4.operator new与operator delete函数
1.new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数
2.new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局 函数来释放空间。
5. new和delete的实现原理
5.1 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
5.2 自定义类型
new的原理
调用operator new函数申请空间
在申请的空间上执行构造函数,完成对象的构造
delete的原理在空间上执行析构函数,完成对象中资源的清理工作
调用operator delete函数释放对象的空间
new T[N]的原理调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请
在申请的空间上执行N次构造函数
delete[]的原理在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间
总结:new == operator new -> (malloc) + 构造函数~~~~~~~~~~ delete == 析构函数 + operator delete -> (free)
~~~~~~~~~~ 特别的:new失败了,抛异常, 不需要再检查返回值
>)delete注意事项
cpp
ClassA *pclassa=new ClassA[5];
delete pclassa;
析构函数此时只会调用1次,要想完整释放数组空间,需要使用[]
注意:程序可能崩溃