当前位置: 首页 > news >正文

常见的内存泄露情况汇总

就好像学习硬件不点亮一颗电容,一块MCU不能说自己学过硬件一样。
没写过内存泄露的代码,不能说自己学习过CPP。w(゚Д゚)w
那场景的内存泄露场景又有哪些?你踩过多少坑呢?
楼主把初学者常见的内存错误问题汇总了一下,希望对你有邦族

总的来说,内存问题只有三类:

问题类型核心特征主要后果
内存泄漏只分配,不释放(失去引用)内存被永久占用,资源耗尽
多重释放一次分配,多次释放立即导致未定义行为(通常崩溃)
悬挂指针(野指针)使用已释放的内存未定义行为(数据损坏、崩溃)

刁钻点来说,只有两类: 内存泄露和未定义行为。这个分类属于见仁见智了。

内存泄露

1. 直接分配未释放

最基本的内存泄漏情况是分配了内存但忘记释放。

void memory_leak() {int* ptr = new int[100]; // 分配内存// 使用 ptr...// 忘记调用 delete[] ptr;
}
2. 异常导致的内存泄漏

当在 new 和 delete 之间发生异常时,delete 可能不会被执行。

void potential_leak() {int* ptr = new int[100];some_function_that_might_throw(); // 如果这里抛出异常delete[] ptr; // 这行不会执行
}

解决方案:使用 RAII 技术或智能指针

#include <memory>
void no_leak() {std::unique_ptr<int[]> ptr(new int[100]);some_function_that_might_throw(); // 即使抛出异常,ptr 也会自动释放内存
}
3. 构造函数中的资源分配未在析构函数中释放
class ResourceHolder {
private:int* data;
public:ResourceHolder() {data = new int[100]; // 在构造函数中分配}// 缺少析构函数 ~ResourceHolder() { delete[] data; }// 内存泄漏:当对象销毁时,data 指向的内存不会被释放
};
4. 缺少拷贝构造函数和拷贝赋值运算符(Rule of Three)

当类管理资源时,如果缺少适当的拷贝控制成员,可能会导致多重释放或内存泄漏。

class BadArray {
private:int* data;size_t size;
public:BadArray(size_t n) : size(n), data(new int[n]) {}~BadArray() { delete[] data; }// 缺少拷贝构造函数和拷贝赋值运算符// 当发生拷贝时,两个对象会指向同一块内存// 析构时会导致双重释放或内存泄漏
};
5.容器中的指针未正确清理
#include <vector>
void container_leak() {std::vector<int*> vec;for (int i = 0; i < 10; ++i) {vec.push_back(new int(i)); // 分配内存}// 使用 vec...// 忘记释放 vec 中的每个指针// 正确做法:在清除 vector 前先释放每个元素for (auto ptr : vec) {delete ptr;}vec.clear();
}

更好的解决方案:使用智能指针容器

#include <vector>
#include <memory>
void no_container_leak() {std::vector<std::unique_ptr<int>> vec;for (int i = 0; i < 10; ++i) {vec.push_back(std::make_unique<int>(i));}// 不需要手动释放,vector 清除时会自动释放所有 unique_ptr
}

双重释放/多重释放 (Double/Multiple Free)

定义:对同一块动态分配的内存释放了多次。

根本原因:多个指针指向同一块内存,且这些指针的生命周期管理混乱,导致同一块内存被多个不同的代码路径释放。

后果立即导致未定义行为。最常见的表现是程序瞬间崩溃(如 glibc 检测到 "double free or corruption"错误),但也可能破坏内存管理器的数据结构,为安全漏洞埋下隐患。

示例

void double_free() {int* ptr1 = new int(5);int* ptr2 = ptr1;   // 两个指针指向同一块内存delete ptr1;        // 第一次释放,OKdelete ptr2;        // 第二次释放同一块内存 -> 双重释放,灾难性错误!
}
多指针造成的垂悬重复泄露问题

简单来说,就是一块自由内存的指针被多个对象持有,不能确定谁在什么时候释放这块内存。 可能已经被释放了,但是别的还持有,别的指针可能还会释放,造成多重释放

void unclear_ownership() {int* data = new int[100];process_data(data); // 这个函数应该负责释放吗?// 如果不应该,那么这里需要释放// 如果应该,那么这里不需要释放// 所有权不明确容易导致双重释放或内存泄漏
}

悬挂指针 (Dangling Pointer) 的使用

定义:指针指向的内存已经被释放,但指针本身仍然被使用(解引用或再次释放)。

根本原因:内存被释放后,指向它的指针没有被及时置空 (nullptr)。

后果未定义行为。读取可能得到垃圾数据,写入会破坏可能已被重新分配给他用的内存区域,导致程序行为诡异且难以调试。

示例

void use_dangling_pointer() {int* ptr = new int(5);delete ptr;         // 内存已释放*ptr = 10;          // 错误!向已释放的内存写入 -> 未定义行为int value = *ptr;   // 错误!从已释放的内存读取 -> 未定义行为
}
返回局部对象的指针/引用
// 错误示例1:返回局部变量的指针
int* create_int_dangerously() {int local_value = 42; // local_value 在栈上分配return &local_value;   // 返回它的地址
} // 函数结束,local_value 的生命周期结束,其内存被回收// 错误示例2:返回局部变量的引用
int& create_int_ref_dangerously() {int local_value = 42;return local_value;    // 返回它的引用
} // 函数结束,同样的问题发生void main() {int* dangling_ptr = create_int_dangerously();int& dangling_ref = create_int_ref_dangerously();// 此时 dangling_ptr 和 dangling_ref 都指向已经被回收的栈内存// 对它们进行任何操作(读取、写入)都是未定义行为 (Undefined Behavior)std::cout << *dangling_ptr << std::endl; // 可能输出垃圾值,也可能崩溃dangling_ref = 100;                      // 可能破坏程序状态,导致难以调试的错误
}

幸运的是,像 GCC 和 Clang 这样的现代编译器通常能检测到这种错误,并会发出类似 warning: address of stack memory associated with local variable 'local_value' returned 的警告。

// 方式二(现代C++最佳实践):返回智能指针,自动管理所有权
#include <memory>
std::unique_ptr<int> create_int_safely() {auto ptr = std::make_unique<int>(42); // 在堆上分配return ptr; // 转移所有权给调用者
}
void main() {auto safe_ptr = create_int_safely();// ... 使用 safe_ptr ...// 无需手动释放!main函数结束时,safe_ptr超出作用域会自动释放内存。
}
多指针造成的垂悬问题

简单来说,就是一块自由内存的指针被多个对象持有,不能确定谁在什么时候释放这块内存。

void unclear_ownership() {int* data = new int[100];process_data(data); // 这个函数应该负责释放吗?// 如果不应该,那么这里需要释放// 如果应该,那么这里不需要释放// 所有权不明确容易导致双重释放或内存泄漏
}

好了,就是这些,在以后楼主遇到更复杂的情况也会收录进来的。如果这对你有用,还请点赞,投币 收藏 关注 一波~ 这对我非常重要ヾ(•ω•`)o

http://www.xdnf.cn/news/1488889.html

相关文章:

  • STM32 开发(三十三)STM32F103 片内资源 —— 直接存储 DMA 实战 编码详解
  • TypeORM 入门教程之 `@OneToOne` 关系详解
  • Day23_【机器学习—集成学习(5)—Boosting—XGBoost算法】
  • Python struct模块 | 使用pack函数进行字节序打包
  • k8s镜像推送到阿里云,使用ctr推送镜像到阿里云
  • Python实战:打造简易人脸识别门禁系统
  • MySQL 主从读写分离架构
  • UserManagement.vue和Profile.vue详细解释
  • Windows 内存整理和优化工具 - Wise Memory Optimize
  • Java初体验
  • 缓存无处不在
  • fps:AI系统
  • 2.TCP深度解析:握手、挥手、状态机、流量与拥塞控制
  • 火山 RTC 引擎15 拉流 推流 地址生成器 、合流转推 开关
  • Vulkan 学习(20)---- UniformBuffer 的使用
  • 【系统分析师】第7章-基础知识:软件工程(核心总结)
  • 计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】
  • 阿里云上启动enclave 并与宿主机通信
  • 韧性双核系统:个人与关系的共生进化框架
  • 2024理想算法岗笔试笔记
  • HTTP中Payload的含义解析
  • MySQL集群高可用架构——组复制 (MGR)
  • Set集合
  • matrix-breakout-2-morpheus靶机渗透
  • 【从零开始学习Redis】秒杀优化——阻塞队列、消息队列实现异步秒杀
  • 虚拟机之CentOS、网络设置的有趣问题
  • openpyxl和excel数据驱动
  • C++20格式化字符串:std::format的使用与实践
  • 大坝安全监测中的单北斗GNSS变形监测系统应用解析
  • 宋红康 JVM 笔记 Day14|垃圾回收概述