1、内存分配方式
在C++中,内存分成五个区,分别是堆、栈、自由存储区、静态存储区和常量存储区。
1) 栈
执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置处理器指令集中,效率很高,但分配的内存容量有限。
2) 堆
由new分配的内存块,释放由程序员控制。如果程序员没有释放,那么就在程序结束的时候,被操作系统回收。
3) 自由存储区
由malloc等分配的内存块,用free结束自己的生命。
4) 静态存储区
全局变量和静态变量被分配到同一块内存中。C语言中,全局变量分为已初始化和未初始化。
5) 常量存储区
里面存放的是常量,不允许修改。
2、堆和栈的区别
int * ptr = (int*)malloc(sizeof(int)*4);
在栈中存放了一个指向堆内存的指针ptr。在程序会先确定在堆中分配内存的大小,然后利用operator new分配内存,然后放回这块内存的首地址,放在栈中。
1) 空间大小不同
在32位系统中,堆的空间是4G,而栈的空间很小。
2) 分配方式不同
堆是动态分配的,没有静态分配。栈的静态分配时编译器完成的,动态分配由alloca函数分配。栈的动态分配由编译器进行释放。
3) 管理方式不同
栈是由编译器自动管理。堆的释放是由程序员控制。
4) 生长方式不同
堆的生长方式是向上的,向着内存地址增加的方向。栈的生长方式是向下的,是向着内存地址减小的方向。
5) 能否产生碎片
对于堆,频繁的new/delete会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
6) 分配效率不同
栈是机器系统系统的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定栈的效率比较高。
堆是C/C++函数库提供的,库函数会按照一定的算法,在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。
3、operator new和operator delete
3.1 为什么要进行重载?
在嵌入式系统中,由于内存的限制,频繁的动态分配不定大小的内存会引起很大的问题以及堆破碎的风险。
当你必须要使用new和delete的时候,你不得不控制C++中的内存分配。你需要用重载的全局new和delete代替系统的内存分配符。需要给类重载new和delete。
一个防止内存破碎的方法是从不同固定大小的内存池中分配不同类型的对象。对单个类重载new和delete,你就可以灵活的控制内存分配。
3.2 重载全局的new和delete操作符
void* operator new(size_t size) { void* p = malloc(size); return (p); } void operator delete(void*p) { free(p); }
3.3 类重载new和delete
为单个类重载new和delete
class TestClass { public: void* operator new(size_t size) { void* p = malloc(size); return p; } void operator delete(void* p) { free(p); } };
为单个类重载new[] 和 delete[]
class TestClass { public: void* operator new[](size_t size) { void* p = malloc(size); return p; } void* operator delete[](void* p) { free(p); } } int main() { TestClass* p = new TestClass[10]; delete [] p; return 0; }
注意:new[] 中的个数参数是数组的大小加上额外的存储对象数目的字节。考虑到内存分配机制的因素,尽量避免使用对象数组。
4、有了malloc/free为什么还要new/delete
malloc和free是C/C++语言的标准库函数,new / delete是C++ 的运算符。都用于申请动态内存和释放内存。
malloc / free 无法满足动态对象的要求。对象在创建的同时同时自动执行构造函数,对象消亡之前自动执行析构函数。即就是C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理和释放内存工作的运算符delete。
内部数据类型的对象没有析构和构造的过程,所以对于他们来说malloc / free和new / delete是等价的。
5、内存耗尽的三种解决方法
在申请动态内存时,找不到足够大的内存块,malloc和new将返回NULL指针。
1)判断指针是否为NULL,如果是则使用 return 终止函数
2)判断指针是否为NULL,如果是则使用 exit(1) 终止程序
3)为malloc和new 设置异常处理函数 。