C++内存管理

1. 内存分配方式

在c++中内存分为5个区, 分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

堆: 堆是需要程序员手动分配和释放,属于动态分配方式。内存空间没有限制, 内存空间不连续,因此会产生内存碎片。操作系统有一个记录内存空间的链表,当收到内存申请遍历链表,找到第一个空间大于申请空间的堆结点,然后将节点分配给程序,并将该节点从链表中删除,如果空间比要分配的空间大,就将剩余空间重新加入到链表中。

栈:局部变量,函数参数等存储在该区,由编译器自动分配和释放内存空间是连续的,但是栈的内存空间有限。

全局/静态存储区:全局变量、静态变量分配到该区,到程序结束时自动释放,包括DATA段(全局初始化区)与BBS段 (全局未初始化段)。其中,初始化的全局变量和静态变量存放在DATA段,未初始化的全局变量和静态变量放在BBS段。BBS段特点:在程序执行前BBS段自动清零,所以未初始化的全局变量和静态变量在程序执行前已经成为0.

文字常量区:存放常量,而且不允许修改。程序结束后由系统释放。

程序代码区:存放程序的二进制代码。

 

常见的内存错误及对策:

内存分配未成功,却使用了它。解决办法:在使用之前检查指针是否为NULL,如果指针p是函数参数,那么在函数入口处assert(p != NULL), 如果是malloc或者是new申请的话, 应该用if(p == NULL)进行防错管理。

内存分配成功,但未初始化就使用它。  解决办法:初始化变量

内存分配成功且已经初始化,但操作越过了边界。 解决办法:边界检查

忘记释放内存。解决办法:含有这个错误的函数每次调用一次就丢失一块内存,造成内存耗尽,记得free或delete

释放了内存却继续使用它。 有三种情况:1.程序中对象的关系过于复杂,难以搞清哪个对象是否已经释放了内存。2.函数中的return写错,返回了指向栈中的指针或者引用。3.free或delete后,没有将指针设置为NULL,产生了野指针。

使用存储区的三种方式:1.静态存储区 全局变量,静态变量类型存储在该区,在编译期间就进行分配,生存期到程序结束。存储在该区的对象只初始化一次,且在程序运行期间地址固定不变。 2.自动存储区。 局部变量, 函数参数等存储在该区,由编译器自动分配和释放。 3.自由存储区。 由程序员手动分配和释放内存。

堆和栈的区别:

1.空间大小:栈的空间是连续的,空间大小是系统预先规定好,即栈顶地址和最大空间是确定的;而堆的内存空间是不连续的,由一个记录空间的链表来负责管理,因此内存空间几乎没有限制, 在32位系统下,内存空间大小可以达到4G。

2. 管理方式:栈是由编译器自动分配和释放,而堆需要程序员来手动分配和释放,若忘记delete,容易产生内存泄漏

3. 生长方向不同:对于栈,它是向着内存地址减小的方向生长,这也是为什么站的空间是有限的,而堆是想着内存地址增大的方向增长的

4. 碎片问题:由于栈的内存空间是连续的,先进先出的方式保证不会产生零碎的空间;而对分配方式是每次在空闲链表中遍历到第一个大于申请空间的节点,每次分配的空间大小一般不会正好等于申请的内存大小,频繁的new操作势必会产生大量的空间碎片。

5.分配效率:栈属于机器系统提供的数据结构,计算机在顶层对栈提供支持,出栈进栈由专门的指令执行,因此效率比较高。而堆是C/C++函数库提供的,当申请空间时需要按照一定的算法搜索足够大小的内存空间,当没有足够的空间时,还需要额外的处理,因此效率比较低。

new和malloc的区别:

1. 申请的内存所在位置

new操作符是从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存就位自由存储区。而堆是操作 系统的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配。

自由存储区是否能够使堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new的实现细节。

2. 返回类型安全性

new操作符内存分配成功时, 返回的是对象类型的指针, 类型严格与对象匹配,无需进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void*, 需要通过强制类型转换将void*指针转换成我们需要的类型。

3. 内存分配失败时的返回值

在new内存分配失败时, 会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时会返回NULL。

try
{
    int *a = new int();
}
catch(bad_alloc)
{
    ....
}

int *a = (int*)malloc(sizeof(int));
if (NULL == a)
{
    ....
}
else
{
    ....
}

4. 是否需要指定内存大小

使用new操作符申请内存分配时无需指定内存块大小,编译器会根据类型信息自行计算,而malloc则需要显示的指出内存所需的内存尺寸。

5.是否调用构造函数/析构函数

使用new操作符来分配对象内存时会经历三个步骤:

第一步:调用operator new 函数分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。

第二步:编译器运行响应的构造函数以构造对象,并为其传入初值。

第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

第一步:调用对象的析构函数。

第二步:编译器调用operator delete函数释放内存空间。

总之来说,new/delete会调用对象的析构函数和构造函数来完成对象的构造和析构,malloc则不会。

6. 对数组的处理

C++提供了new[]与delete[]来专门处理数组类型:

A *ptr = new A[10]; //分配10个A对象

使用new[]分配的内存必须使用delete[]进行释放:

delete []ptr;

new 对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为二米个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象 部分释放的现象,造成内存泄漏。

至于malloc,它并不知道你在这块内存要放的数组还是别的东西,反正就是给你一块原始的内存,再给你个内存地址就完事。所以如果要动态分配一个数组的内存,需要我们手动自定数组的大小。

7.new 与 malloc是否可以相互调用

operator new / operator delete的实现可以基于malloc, 而malloc的实现不可以去调用new。

8. 是否可以被重载。

operator new / operator delete可以被重载。

//这些版本可能抛出异常
void * operator new(size_t);
void * operator new[](size_t);
void * operator delete (void * )noexcept;
void * operator delete[](void *0)noexcept;
//这些版本承诺不抛出异常
void * operator new(size_t ,nothrow_t&) noexcept;
void * operator new[](size_t, nothrow_t& );
void * operator delete (void *,nothrow_t& )noexcept;
void * operator delete[](void *0,nothrow_t& )noexcept;

而malloc/free并不允许重载。

9.能够直观的重新分配内存

使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指的内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有的数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。

10. 客户处理内存分配不足

在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误 处理函数,这就是new_handler。new_handler是一个指针类型。

namespace std
{
    typedef void (*new_handler)();
}


 指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明的一个标准库函数:

namespace std()
{
    new_handler set_new_handler(new_handler p) throw();
}

set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时调用该函数。返回值也是个指针,指向set_new_handler被调用前正在执行的那个new_handle函数。

对于malloc,客户并不能去编程解决内存不足以分配时要干什么事,只能看着malloc返回NULL;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值