一篇大佬写的超级详细的C++内存管理的教学,链接: https://blog.csdn.net/caogenwangbaoqiang/article/details/79788368
整理笔记备忘。
1 内存管理
1.1 内存分配方式
栈、堆、自由存储区、静态/全局存储区、常量存储区
- 栈:由编译器维护,用于存放局部变量
- 堆:由程序员自行维护,对应的操作是new()和delete()
- 自由存储区:存放由malloc()/free()管理的内存
- 静态/全局存储区:静态变量static和全局变量存放的位置
- 常量存储区:只存放常量的内存空间
1.2 栈和堆的区别
栈 | 堆 |
---|---|
空间小(1M左右) | 空间大(4G) |
由编译器维护 | 由程序员自行维护 |
有专用的寄存器和指令,分配效率高 | 由C/C++函数库实现,有复杂的空间分配算法,分配效率低 |
不易产生内存碎片(栈结构) | 频繁地new和delete会导致大量的内存碎片,程序执行效率会下降 |
内存地址由高地址向低地址生长(先进后出) | 内存由低地址向高地址生长 |
静态/动态分配(alloc) | 动态分配 |
1.3 使用内存时要注意
- 对于分配的内存,要校验是否分配成功
- 对分配成功的内存要赋初值
- 使用时要防止越界,做边界检查
- 动态内存操作必须配对,new/delete,malloc/free
- 释放了的内存不能继续使用
(1) 指针指向的内存释放之后,指针没有置为NULL,会变成野指针
(2) 在return返回值时避免返回局部变量的指针或引用,因为局部变量在该函数执行完成之后就会被释放
1.4 指针和数组的对比
- 内容修改的区别
数组“对应”着一段内存空间,其内容可以通过对数组的操作进行自由修改
指针“指向”一段内存空间,指针本质是变量,保存着所指向的数据的地址,指针指向的数据能否修改,取决于这个数据的类型,如果是常量则不可修改,这种错误编译器检查不出来,但实际运行时会出错 - 内容复制和比较
数组复制不能用a=b,需要用strcpy()函数,数组的比较也要使用strcmp()函数
指针复制可以用p=q,但不是将数据复制,而是将q指向的数据存放的地址拷贝给p,如果修改这个数据,则*p,*q的值都会发生变化,如果想将数据进行拷贝,可以用malloc函数为p申请一块大小为sizeof(T)(strlen(*q)+1)的内存,再通过strcpy函数拷贝,比较大小也是要用到strcmp函数
sizeof(数组),得到的是数组内数据的长度(字节)
sizeof(指针),得到的是该指针的类型的长度
注意,当数组作为函数的参数传递时,会自动退化为同类型的指针,此时sizeof函数无法得到这个数组的大小
1.5 指针参数传递内存
如果指针作为一个函数的参数,在这个函数内为指针申请内存是不会改变源指针的,因为编译器会为函数的参数创建副本_p,申请内存只会使_p得到一段内存(实际上这块内存在使用之后并没有销毁,也就是产生了内存泄露)
如果想要在函数里为指针申请内存,就需要传入指向指针的指针&p,或者使用函数返回值来传递p,但参见上文,不能将指向栈空间的指针作为返回值传递,需要使用new或malloc申请内存
1.6 野指针
野指针是指向“无用的”数据的指针,野指针产生的情况可能有:
- 指针没有初始化,创建指针变量的时候一定要赋值
- 在delete或free释放指针指向的内存空间后,没有将指针置为NULL
- 指针指向的内容生命周期结束,而指针本身还在使用,此时的指针也会变成野指针
1.7 malloc/free和new/delete的区别
- malloc/free是标准库函数,new/delete是运算符
- C程序只能用malloc/free管理动态内存
- 非内部数据类型的对象,new/delete执行时会调用其构造函数和析构函数,而内部数据类型不存在构造和析构函数。
- 其实new和delete的底层还是malloc和free
1.8 内存耗尽
当new/malloc函数返回一个空指针时表示无法申请一段指定大小的内存,此时需要使用return终止该函数或exit(1)终止整个程序
32位以上的系统基本不存在内存耗尽的情况(因为虚拟内存技术,当内存不够用时,调用硬盘空间作为虚拟内存),但还是要在申请内存时检查申请是否成功
1.9 new/delete使用注意事项
使用new时会自动调用类的构造函数进行初始化,当构造函数不唯一时,new也支持通过参数类型和数量执行所需的构造函数,如
Obj *obj=new Obj;
Obj *obj=new Obj(参数列表);
注意在new一个Obj[]类型的对象时无法指定参数,只能调用默认的无参数构造函数,在之后要释放该对象时,要同样调用delete[]而不是delete
Obj *obj=new Obj[length];
delete []obj