1.内存分配方式总结
前言先说说内存的分配方式,分为堆,栈、全局/静态存储区、常量存储区、自由存储区
1)栈。函数局部变量存储单元在栈上创建。因为局部变量的生存周期,在函数结束的时候,栈上存储单元被释放。
特点:由于运算符内置于处理器的指令集中,效率很高,分配的内存容量有限。
2)堆。new创建的变量,分配的内存块。
特点:需要手动去释放变量的内存块。程序结束后,操作系统会自动回收。
3)自由存储区。malloc分配的内存块
特点:与堆相似,需要用free去释放。
4)自由存储区。创建的全局变量和静态变量,全局变量分为初始化和未初始化,共同在一块内存区
特点:全局变量和静态变量
5)常量存储区。存放常量
特点:不允许修改
2.堆与栈的区别之处
eg: void f(){ int *p=new int[8]; } //分配了一块栈内存,意思是:在栈内存上存放一块堆内存的指针p
release rel(){ delete[] p;} //释放申请的内存
区别之处:
1)管理方式不同。栈由编译器自动管理;堆由程序员手动释放,已发生内存泄漏。
2)空间大小不同。栈控件一般是1M;堆内存可以达到4G。也可以通过配置进行修改。
3)能否产生碎片不同。栈是线进后出的队列,内存不易产生碎片;堆频繁进行new和delete,造成空间不连续,易产生碎片,程序效率降低。
4)生长方向不同。栈向下,向内存地址减小的方向增长;堆向上,向着内存地址增加的方向。
5)分配方式不同。栈分为静态和动态分配,静态由编译器完成,动态由alloc函数分配,编译器释放;堆动态分配
6)分配效率不同。栈由于底层的支持,有专用寄存器存放栈地址,压栈和出栈有专门的指令执行,效率高;堆根据函数库的算法申请内存空间,效率低。
3.防止堆破碎的通用方法
从不同固定大小的内存块中分配不同类型的对象。对每个类重载new和delete提供这样的控制。
重载eg:
void *operator new(size_t size){ void *p=malloc(size); return p;}
void operator delete(void *p) { free p;} //使用malloc()和free()来实现重载
使用对单个类的new和delete进行重载
class TestClass
{
public:
void *operator new(size_t size){ void *p=malloc(size); return p;}
void operator delete(void *p) { free p; }
void *operator new[](size_t size){ void *p=malloc(size); return p;}
void operator delete(void *p) { free p; }
}
4.解决内存出错的方法
1)内存分配未成功,却使用了它。
解决办法:使用内存之前检查指针是否为NULL。
使用assert(p!=NULL);如果使用malloc和new来申请内存,则使用if(p==NULL)或者if(p!=NULL)进行防错处理。
2)内存分配虽然成功,但尚未进行初始化就引用它。
解决办法:起因:一是没有初始化观念,二是误认为内存缺省初值为0。
一般去进行初始化为0。
3)内存分配成功并且进行了初始化,但操作越过了内存的边界
解决办法:注意“多1”或者“少1”的操作
4)忘记了释放内存,造成内存泄漏。
解决办法:(起因:函数每调用一次就丢失一块内存,程序会突然提示:内存耗尽)
malloc和free要配对使用;new和delete要配对使用
5)释放了内存却仍在使用它
解决办法:
情况一:调用关系复杂,不清楚某个对象是否释放了内存。需要重新设计数据结构,从根本上解决对象管理混乱局面
情况二:return语句写错。不要返回“栈内存”的“指针”或者“引用”,函数结束时被自动销毁
情况三:使用free和delete释放后,没有置NULL。导致产生野指针。
5.指针参数是如何传递内存的?
若要指针参数去申请内存,使用指针的指针
eg:void GetMemory2(char **p,int num)
{
*p=(char *)malloc(sizeof(char *)*num);
}
char *str=NULL;
调用GetMemory2(&str,100); strcpy(str,"hello"); free(str);
6.有了malloc和free,为什么还要new/delete
malloc和free是C/C++语言的标准库函数,new/delete是c++的运算符。但是malloc和free如何满足动态对象的要求。
引入new进行完成动态内存分配和初始化工作,引入delete完成清理与释放内存工作的运算符delete。
7.既然new/delete完全覆盖了malloc/free,为何malloc和free没有被淘汰
因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
8.内存耗尽该怎么办?
1)判断指针是否为NULL,如果是,则用return语句终止本函数。
2)判断指针是否为NULL,如果是,使用exit(1)终止整个程序的运行。
3)为new和malloc设置异常处理函数