从分配内存到释放内存整个过程的角度看一下这四个的区别,首先明确一下
new和delete这两个是C++提供的关键字或者说操作符、而malloc和free是C语言的库函数
分配内存之初
如何使用
(1)[T* p = new T]: new + 一个对象类型---> 会自动计算所需分配的内存大小
(2)[T* p = (T*)malloc(sizeof(T))]: malloc + sizeof对象类型--->需要手动输入所需分配的内存大小
底层原理:
(1)new
首先重载new运算符
然后申请足够的内存空间
然后调用构造函数会初始化成员变量(malloc不会)
// new T 实际等价于:
void* mem = operator new(sizeof(T)); // 分配原始内存
T* p = new (mem) T(); // 调用构造函数(placement new)
(2)malloc会执行系统调用
使用brk或者mmap分配函数分配内存空间。
分配过程
分配的位置
首先需要清楚内存四区

用户空间低位往上看,我们写的代码被存放在最低位的代码区,像极了(底层牛马)般的砖垒在这里;
再往上是常量区,代码里面有我们创建的最基础的常量,就被放在这里面,属于砖的必备材料;
下两层将地基搭起来后继续往上看
接下来就是全局区,存放一些全局的变量,相当于缝合砖块的水泥
然后是可以变化的堆区,堆区是从下往上增长的,存放牛马们自己分配的内存,可以灵活变化
最后是栈区,这个区域的上面是内核空间,我们不能触碰,因此从那里划个最高界限,栈区从上往下增长,这个区域就是一些局部变量,形参、返回值,是编译器自动分配的。
了解完内存四区,其实还需要知道什么是虚拟内存,什么是物理内存。这里不做详细描述,只需要知道虚拟内存如其名,是虚拟的,人为构想的,需要将虚拟内存映射成物理内存,才算是落实,才能用。

回到今天的主题,刚才说了牛马只能在堆上操作,因此new和malloc其实都是在堆上分配,但是还有许多细节以及区别:
(1)new是在自由存储区free store上分配内存,执行new表达式会立即进行内存分配,并且由于调用了构造函数初始化了成员变量,会直接将虚拟内存映射成物理内存,因此可以理解为new结束后会得到物理内存。
(2)malloc分配过程比较复杂,当使用malloc时,也会立即进行内存分配,但是不会初始化成员变量,因此一开始分配的是虚拟内存,不能立即得到物理内存,如果要得到物理内存需要在初始化的时候触发缺页中断来做这个映射操作。因此可以理解为malloc之后不一定得到物理内存。
关于malloc还有几点需要知道
malloc会维护一个内存池,可以使用brk(),如果分配的内存比较小可以直接从内存池分配,如果内存池没有足够空间再考虑系统调用。主要是避免频繁系统调用的消耗。
对于其他小于128k的空间,使用的是brk,就直接在堆上分配。
对于大于128k的空间,使用的是mmap,会在文件映射区分配(后面说);

分配结束后
分配成功
(1)new返回的是对象类型的指针
(2)malloc返回的是void,因此在使用的时候,可能会需要进行类型转化,所以前面创建的时候,malloc前面加了(T)
分配失败
(1)new会抛出异常
(2)malloc不会抛出异常,返回的是NULL
释放内存
如何使用
(1)[delete p] delete + 刚才创建的对象类型指针
(2)[free(p)] free + (void*类型指针)
底层原理:
(1)delete
首先调用析构函数-->需要知道对象类型
然后重载delete运算符执行删除操作
最后释放空间
// delete p 实际等价于:
p->~T(); // 调用析构函数
operator delete(p); // 释放内存
(2)free
传给free的是一个没有类型的指针void*,此时如何知道要删除多大内存空间呢?
实际上malloc分配空间的时候多分配了16字节空间,后面跟的才是具体分配对象的区域。返回的是后面对象区域的首地址,拿到首地址在释放的时候会先左偏移16字节,前面这16字节的作用是存储内存块的描述信息,并且包含了后面的长度,这样就知道要释放多少内存。

增加一个问题:经过free释放后,内存还在吗?
如果是内存池分配的空间会直接回到内存池,如果是系统调用分配的空间,操作系统会立即释放内存。
补充:
1、C++中如果是申请内置类型的对象或是数组,用new/delete和malloc/free没有什么区别。
2、如果是自定义类型,区别很大,new和delete分别是开空间+构造函数、析构函数+释放空间,而malloc和free仅仅是开空间和释放空间。
3、建议在C++中无论是内置类型还是自定义类型的申请和释放,尽量都使用new和delete。

整体使用例子
(1)new/delete
class MyClass {
public:
MyClass() { std::cout << "Constructor\n"; }
~MyClass() { std::cout << "Destructor\n"; }
};
int main(){
MyClass* p = new MyClass;
delete p;
return 0;
}
(2)malloc/free
typedef struct {
int x;
char name[10];
} MyStruct;
int main() {
// 分配内存(不会调用构造函数)
MyStruct* p = (MyStruct*)malloc(sizeof(MyStruct));
// 使用(注意必须手动初始化)
p->x = 42;
strcpy(p->name, "test");
// 释放内存(不会调用析构函数)
free(p);
return 0;
}
检验学习成果
🧩问题① 使用new/delete的底层原理是什么?为什么给delete传入的是对象类型指针,不能是void*类型指针?
🧩问题② malloc是怎么分配空间的?
🧩问题③ malloc分配的是物理内存还是虚拟内存?调用后会不会立即得到物理内存?
🧩问题④ free(p)是怎么知道要释放多大的内存空间
🧩问题⑤ free释放内存后,内存还在不在?
快速解答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在释放空间前会调用析构函数完成空间中资源的清理。
浙公网安备 33010602011771号