内存分配与管理
内存分配方式
由C/C++编译的程序占用的内存分为以下几个部分
栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量等。其操作方式类似于数据结构中的栈。
堆区(heap):一般由程序员分配释放(malloc/free、new/delete),若程序员不释放,程序结束时可能由操作系统回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
全局区(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。
文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。
程序代码区:存放函数体的二进制代码
堆和栈区别
申请方式不同。
栈由系统自动分配。
堆由程序员手动分配。
申请大小限制不同。
栈向低地址方向增长,栈顶和栈底是之前预设好的,大小固定。
堆向高地址方向增长,是不连续的内存区域,大小可以灵活调整。
申请效率不同。
栈,速度快,是一个先进后出的队列,进出一一对应,不会产生碎片。
堆,速度慢,频繁的new/delete会造成大量碎片,使程序效率降低。
动态内存
malloc、calloc、realloc、alloca
malloc:申请指定字节数的内存。申请到的内存中的初始值不确定。
calloc:为指定长度的对象,分配能容纳其指定个数的内存。申请到的内存的每一位(bit)都初始化为0
realloc:更改以前分配的内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定
alloca:在栈上申请内存。程序在出栈的时候,会自动释放内存。但是需要注意的是,alloca不具可移植性, 而且在没有传统堆栈的机器上很难实现。alloca不宜使用在必须广泛移植的程序中,。C99中支持变长数组(VLA), 可以用来替代alloca()。
malloc、free
申请内存,确认是否申请成功
char *str = (char*) malloc(100); assert(str != nullptr);
释放内存后指针置空
free(p); p = nullptr;
new、delete
new/new[]:完成两件事,先底层调用malloc分了配内存,然后创建一个对象(调用构造函数)。
delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用free释放空间。
new在申请内存时会自动计算所需字节数,而malloc则需我们自己输入申请内存空间的字节数。
int main() { T* t = new T(); // 先内存分配 ,再构造函数 delete t; // 先析构函数,再内存释放 return 0; }
内存泄露
堆是动态分配内存的,并且可以分配很大的内存,使用不好会产生内存泄露。频繁使用malloc和free会产生内存碎片。
所谓内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。一般常说的内存泄漏是指堆内存的泄露。内存泄露其实并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由哪些可以获得程序源码的程序员才可以分析出来。
应用程序一般使用malloc、calloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责响应地调用free、delete释放内存块,否则这块内存就不能再次使用,造成内存泄露。
new/delete和malloc/free区别
new/delete是c++关键字,需要编译器支持;malloc/free是库函数,需要头文件支持;
new能够自动计算需要分配的内存空间,而malloc需要手工计算字节数。例如,
int* p1=new int[2],int * p2=malloc(2*sizeof(int))
;new与delete直接返回具体类型的指针,而malloc与free返回void类型指针。
new是类型安全的,而malloc不是,例如,int * p=new float[2], 编译时就会报错;而
int * p=malloc(2*sizeof(int))
, 编译时编译器就无法指出错误来。new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
指针相关
引用和指针
引用只是别名,不占用具体存储空间,只有声明没有定义;指针是具体变量,需要占用存储空间。
引用在声明时必须初始化为另一变量,一旦出现必须为typename refname &varname形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。
不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。
指针数组和数组指针
指针数组,是指一个数组里面装着指针,也即指针数组是一个数组。一个有10个指针的数组,其中每个指针指向一个整型数,那么次数组定义位: