当前位置:   article > 正文

C++内存泄漏/内存越界的各种情况,以及预防与排查

内存越界

目录

一、内存泄漏

一、介绍

二、几种内存泄露的场景

三、预防与排查

1、valgrind

二、内存越界

一、介绍

二、几种内存越界的情况

三、预防与排查


一、内存泄漏

一、介绍

  • 内存泄漏,是指在程序代码中动态申请的、堆上的内存 由于某种原因、在使用后没有被释放,进而造成内存的浪费。
  • 少部分的内存泄漏不会影响程序的正常运行,不过如果是持续的内存泄漏会耗光系统内存,最终会导致程序卡死甚至系统崩溃。为了避免系统崩溃,在无法申请到内存的时候,要果断调用exit()函数主动杀死进程,而不是试图挽救这个进程。

二、几种内存泄露的场景

1、malloc/new申请的内存没有主动释放

  1. void test1()
  2. {
  3. char* str = new char[100];
  4. /*delete[] str; 这里忘记delete了 */
  5. }

2、new与free混用,malloc与delete混用

  1. class Base
  2. {
  3. public:
  4. int* values;
  5. Base() { values = new int[100]; }
  6. ~Base() { delete[] values; }
  7. };
  8. void test2()
  9. {
  10. Base* pBase = new Base;
  11. free(pBase);
  12. /* 错误,这样只会释放pBase指向的内存,却不会调用Base的析构函数
  13. 会导致Base中的values指向的内存无法被释放 */
  14. delete pBase; /* 正确 */
  15. }

3、使用new开辟数组时,delete忘记加[]

  1. void test3()
  2. {
  3. int* vects = new int[100];
  4. delete vects; // 这种写法是错误的
  5. delete[] vects; // 这种写法是正确的
  6. }

4、基类的析构函数没有定义为虚函数

  1. class A
  2. {
  3. public:
  4. A() {}
  5. ~A() {}
  6. };
  7. class B : public A
  8. {
  9. public:
  10. B() { num = new int[100]; }
  11. ~B() { delete[] num; }
  12. private:
  13. int* num;
  14. };
  15. void main() {
  16. A* pa = new B();
  17. delete pa;
  18. /* 这样只会调用A类的析构函数,而B类中的num就得不到释放
  19. 正确的是应该把A的析构类声明为虚函数 */
  20. }

三、预防与排查

1、valgrind

  • 应用环境:Linux
  • 编程语言:C/C++
  • 使用方法:  编译时加上-g选项,如 gcc -g filename.c  -o filename,使用如下命令检测内存使用情况:
  • 结果输出:#valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./filename,就会看到内存使用报告
  • 设计思路:根据软件的内存操作维护一个有效地址空间表和无效地址空间表(进程的地址空间)
  • 优缺点:能够检测:
    • 使用未初始化的内存 (Use of uninitialised memory)
    • 使用已经释放了的内存 (Reading/writing memory after it has been free’d)
    • 使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
    • 对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
    • 申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
    • malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
    • src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
    • 重复free
  • 如何获取:http://valgrind.org/

2、使用智能指针

  1. void test4()
  2. {
  3. // 以下两种写法效果相同
  4. shared_ptr<int> p1 = shared_ptr<int>(new int(234));
  5. shared_ptr<int> p2 = make_shared<int>(234);
  6. // 还有其他指针指针
  7. weak_ptr<int> p3 = weak_ptr<int>(p1);
  8. unique_ptr<int> p4 = unique_ptr<int>(new int(234));
  9. auto_ptr<int> p5 = auto_ptr<int>(new int(234));
  10. }

二、内存越界

一、介绍

  • 内存越界是软件系统主要错误之一,其后果往往不可预料且非常严重。更麻烦的是,它出现的时机是随机的,表现出来的症状是随机的,而且造成的后果也是随机的,这会使程序员很难找出这些 Bug 的现象和本质之间的联系,从而给 Bug 的定位带来极大的困难。一般情况下,内存越界访问可分如下两种:
  • 读越界,即读了不属于自己的数据。如果所读的内存地址是无效的,程序立刻崩溃;如果所读内存地址是有效的,在读的时候不会马上出现问题,但由于读到的数据是随机的,因此它会造成不可预料的后果。
  • 写越界,又称为缓冲区溢出,所写入的数据对别的程序来说是随机的,它也会造成不可预料的后果。

二、几种内存越界的情况

  1. 定义指针的时候未初始化,所以指针指向的时一块随机值,用户并不一定有访问权限。
  2. 分配到的内存比实际上使用的内存要小。
  3. 使用下标访问数组时,下标错误。
  4. 内存已经被释放了,但仍指针来使用这块内存。

三、预防与排查

  1. 定义指针变量的时候,如果暂时不初始化,可以先让它指向 nullptr。
  2. 分配内存时,用户要检查内存是否分配成功。
  3. 使用下标访问数组的时候,要检查是否越界。
  4. 内存被释放之后,指向这块内存的指针也要赋值为 nullptr。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/143529
推荐阅读
相关标签
  

闽ICP备14008679号