C/C++中程序中内存区域简单划分及介绍
内核空间:存放内核代码和数据的空间,提供给内核使用
栈(堆栈):存储非静态局部变量/实例化后的形参/返回值等等数据的空间,栈向下增长(即从高地址处向低地址处生长)。
内存映射段:用于高效映射I/O映射,装载共享动态库的空间。用户可使用系统接口创建共享内存,做进程间通信
堆:在程序运行时用于动态分配的空间,堆向上增长,程序员申请,必须由程序员释放。
数据段:存储全局数据与静态(static
)数据,程序结束时由系统释放
代码段:可执行的代码/只读常量
这篇文章主要集中于C++内存管理,如果有铁汁儿想先回忆一下C语言动态内存管理,移步此处动态内存管理
C++内存管理方式
在C++中,我们仍然可以使用C语言的库函数malloc
/calloc
/realloc
实现内存分配。
但C++有更好的方法——new
运算符,再次强调!运算符new
!!!
我们先来试试叭!
使用new分配内存
在运行阶段为一个int值分配未命名的空间,使用指针去访问这个值。
int* pt = new int;
new int
告诉程序,需要适合存储一个int
数据的空间。new
根据类型确定需要多少字节的内存,找到后返回该空间的地址。
为一个数据对象(内置/自定义)获得并制定分配内存的格式如下:
typeName* pointer_name = new typeName;
上面这个仅是申请一个typeName
型数据空间的通用格式
那么申请了,释放怎么办?往下看
使用delete释放内存
我们使用完new
得到的内存后,当然要释放了!因为new申请的空间仍然在堆上,所以我们还是要自己手动释放,此时应用到delete
,将空间归还给系统。这与malloc
与free
并无二致,自己申请,自己释放。
使用delete
释放空间,要给delete
后加上被申请空间的指针,就像free释放malloc申请的空间一样,需要指针的传入。如下:
typeName* pointer_name = new typeName;
{
//业务处理完成}
delete pointer_name;//delete释放空间
[Note]:
1.只能用delete释放new申请的空间!这两个是cp,不能拆开,同理,malloc与free也是cp,它们不能混搭使用。
2.不要尝试用delete释放已经释放了的空间! C++标准指出,这么做的结果是不确定的。但对nullptr
来讲是安全的,也就是说,除nullptr外,其它指针不能被释放两次及以上。
使用new申请动态数组
通用格式如下:
typeName* pointer_name = new typeName[number];
这条语句在程序运行时会申请一个可以存放number个typeName
类型数据的空间,并将该空间的地址赋给pointer_name
,之后我们就能使用申请的空间了。
但我们应知道,一般来说,在C语言里,一般动态申请空间时,会这么写:
typeName* pointer_name = (typeName*)malloc(sizeof(typeName)*number);
if(pointer_name){
//申请成功
//业务处理
}
else{
//申请失败
//异常处理
}
也就是说在C语言里申请动态空间,是否成功需要我们自己判断,然后处理。
但在C++里使用new,我们并不需要自己判断是否申请成功。这是因为new申请时会自己判断:
·申请成功,成功直接返回地址,
·不成功,进行内存不足的处理模块,清理无用空间,继续申请,成功则返回地址,不成功则抛出异常。
是不是很nice!我们又能少写几行代码了!
使用delete释放动态数组
申请了动态数组,释放如下:
typeName* pointer_name = new typeName[number];
{
//业务处理完成}
delete []pointer_name;//delete释放空间
看仔细了!delete []poimnter_name
,释放目标之前有一个[]
!!!。方括号告诉程序,要释放的目标是一个数组,不是一个元素!方括号里填不填number无所谓!你想填多少也无所谓!因为此处delete
只会用到[]
,不管里面有没有数字/数字是多少
[Note]:
1.不要使用delete
释放非new
申请的内存空间
2.不要使用delete
释放同一块空间两次
3.如果使用new
分配空间,应使用delete
释放
4.如果使用new[]
分配空间,则应使用delete[]
释放
5.对nullptr
来说,delete
用多少次都是安全的
使用new与delete为类的实例化对象管理空间
现有一个类T,我们想要申请可以存放number个T实例化后的空间,如下即可:
T* pointer_name = new T[number];
但为类实例化对象申请空间有以下两步:
第一步:调用operator new函数申请空间
第二步:在申请的空间上执行number次构造函数,完成number个对象的构造
释放空间如下即可:
T* pointer_name = new T[number];
{
//业务处理}
delete []pointer_name;//delete释放空间
但为类实例化对象释放空间有以下两步:
第一步:在申请的空间上执行number次析构函数,完成number个对象的析构
第二步:调用operator delete函数释放空间
在这里new/delete与malloc/free的一些差别就有所体现了:
new/delete释放空间会调用构造/析构函数
malloc/free与new/delete的区别
比较项 | new/delete | malloc/free |
---|---|---|
申请空间所在内存区域 | 堆 | 堆 |
释放方式 | 手动释放 | 手动释放 |
是否可以初始化 | 可以 | 不可以 |
申请空间大小如何计算 | 只需空间类型即可 | 手动计算并传入 |
返回值是否需要强制类型转换 | 不需要 | 需要 |
申请失败返回值 | 抛出异常 | NULL |
申请自定义类型对象 | 会调用构造/析构函数 | 只开辟空间 |