一、内存区域
内存区域主要可以划分为四个区域:代码区、数据区、栈区、堆区。
代码区(常量区):存放函数体的二进制代码,这部分由操作系统进行管理。当程序运行结束后,这个区域会被释放。代码区是只读的,以防止意外修改代码。
数据区(或称为全局区、静态区):存放全局变量和静态变量以及常量。
栈区:由编译器自动分配和释放,主要存储函数的参数值、局部变量等。
堆区:由程序员分配和释放(如使用malloc、free、new、delete等函数)。
简单来说,就是全局变量和静态变量存放在数据区,普通的变量在栈区,通过malloc、new申请的在堆区,可执行代码和可读常量在代码区。
接下来举几个容易混淆的例子来说明:
在这个例子中,a是全局变量,b是静态变量,所以它们在数据区,p、pc、arr很容易知道在栈区,那*p、*pc、*arr呢?
*p在堆区,因为是malloc出来的;*pc在代码区,我们要知道const char* pc实际上是针对所指向的内容,即*pc不可修改,既然*pc不可修改便是在代码区;*arr在栈区,指向栈区中的数组内容。
二、new/delete运算符
在C语言中我们向堆区申请空间是通过malloc来实现的,释放内存是通过free来实现的。而在C++中,提供了一个运算符new和delete来实现。
我们先来看看这两个运算符的用法:
int* pn = new int;//申请
delete pn;//释放
在C语言中我们需要写要申请的空间的字节数,但是在C++中就没有那么麻烦了,写类型就可以了,在上面两个语句中,便是申请了四个字节的空间并释放 。
对于基本数据类型数组的动态生成和释放:
int* parray = new int[size];//size根据需要的大小来写
delete [] parray;
需要注意的是,要成对地写,否则可能会出错。
我们使用new来开辟空间时也是可以初始化的:
例如:
int* pn = new int(5); //初始化为5
int* parray = new int[10] {1,2,3,4,5};//与C语言中数组初始化类似,未给出的默认为0
对于基本数据类型,malloc与new并无太大差别,但是对于自定义类型差别就很大了。
malloc只会分配内存,并不会初始化,但是new在分配内存的同时会调用构造函数初始化;在释放内存时,delete也会先调用对象的析构函数,然后再释放
举个例子来看:
class A
{
private:
int _a;
public:
A(int x = 0)
:_a(x)
{}
};
int main()
{
A* p = new A;
delete p;
return 0;
}
通过调试也可以看到确实走了构造函数,最后初始化为0了。
malloc和new还有一点不同,使用new操作符时,如果申请内存失败了,会抛出异常,就避免了malloc时自己写if语句来检查。(下图来自文心一言)
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new和operator delete实际上与malloc和free是相同的。
可以理解为new = operator new(malloc)+构造函数,delete = operator delete(free)+析构函数。