内存泄漏和野指针是 C/C++ 中两种不同的内存管理问题。
野指针:指向无效内存的指针(主要由于未初始化、越界访问、已经释放但指针没有置空(悬空指针)导致)
内存泄漏:分配了内存,但是使用完没有释放内存,导致内存无法再次被使用。(主要是由于使用new/malloc后忘记delete/free、或者没有运行到delete就结束了)
野指针不直接导致内存泄漏 ,内存泄漏也不直接导致野指针
内存泄漏
什么是内存泄漏?
内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害?
长期运行的程序出现内存泄漏,影响很大。会导致响应越来越慢,最终卡死。
举例说明导致内存泄漏的情况
void MemoryLeaks()
{
// 1.内存申请了忘记释放,没有delete
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题,有delete,但是中间Func()函数结束了程序,没有运行到delete
int* p3 = new int[10];
Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
delete[] p3;
}
内存泄漏分类
(1)堆内存泄漏
堆内存指的是程序执行中通过malloc、calloc、realloc、new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete释放。假设程序的设计错误导致这部分内容没有被释放,那么以后这部分空间将无法再被使用,就会产生堆内存泄漏。
(2)系统资源泄漏
指程序使用系统分配的资源,比如套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
如何避免内存泄漏
1、事前预防型。如养成良好的编码规范、智能指针等。
2、事后查错型。如泄漏检测工具。
如何申请4G内存?
在32位的平台下,内存大小为4G,但是堆只占了其中的2G左右,所以我们不可能在32位的平台下,一次性在堆上申请4G的内存。这时我们可以将编译器上的win32改为x64,即64位平台,这样我们便可以一次性在堆上申请4G的内存了。
野指针
什么是野指针?
野指针是指向无效地址(不确定或非法内存区域)的指针。这种指针可能指向任意的内存地址,包括系统保留的地址,因此它的行为是不可预测的,可能导致程序崩溃或数据损坏。
野指针是如何产生的?
(1)未初始化的指针
声明了指针变量但是没有赋予确切的地址,值是随机的(可能指向非法内存地址)->此时不能直接解引用,会导致未定义行为。
int* ptr; // 未初始化,ptr 的值是随机的(野指针)
*ptr = 10; // 危险!访问随机内存,可能导致崩溃
解决方法:
①始终初始化指针
int* ptr = nullptr; // 初始化为 nullptr
②在解引用之前可以先判断一下是否为空指针
if (ptr != nullptr) {
*ptr = 10; // 安全访问(这里如果ptr是nullptr, 不会执行)
}
(2)指针越界
指针操作超出了其所指向的内存区域(如数组越界),导致访问非法内存。
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
// 越界访问(arr 只有 5 个元素,ptr[5] 是非法内存)
int val = ptr[5]; // 未定义行为(可能是垃圾值或崩溃)
// 另一种越界:指针递增超出范围
for (int i = 0; i < 10; i++) {
std::cout << *ptr << std::endl;
ptr++; // 最后几次访问越界
}
解决方法:
①确保指针访问是在合法的范围内
for (int i = 0; i < 5; i++) { // 正确:不越界
std::cout << ptr[i] << std::endl;
}
②使用STL容器(如std::vector、std::array)替代原生数组,因为他们可以提供边界检查
(3)指针指向的内存被释放后未置空--->悬空指针
指针指向的内存已被 delete 或 free,但指针仍保留原地址,变成悬空指针(Dangling Pointer)。解引用悬空指针会导致UB(可能访问到已经释放的内存或者新分配到这个内存的数据)
情况一:delete 后未置空
int* ptr = new int(10); // 动态分配内存
delete ptr; // 内存释放,但 ptr 仍指向原地址
*ptr = 20; // 危险!访问已释放的内存(UB)
情况二:返回局部变量的指针
int* getLocalPtr() {
int x = 10;//局部变量
return &x; // 返回局部变量的地址
}
int* ptr = getLocalPtr(); // 函数使用完后,局部变量 x 已被销毁,ptr 是悬空指针
std::cout << *ptr; // 未定义行为(可能是垃圾值或崩溃)
情况三:迭代器失效(如 vector 修改后)
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 获取迭代器
vec.push_back(4); // 可能导致迭代器失效(内存重新分配)
//达到vector的容量,会需要重新分配内存
std::cout << *it; // 危险!可能是悬空指针
解决方法:
(1)delete 后立即置空:
delete ptr;
ptr = nullptr; // 防止后续误用
(2)避免返回局部变量的指针(改用返回值或智能指针):
int getValue() { // 返回副本而非指针
int x = 10;
return x;
}
(3)使用智能指针(自动管理内存):
std::unique_ptr<int> ptr = std::make_unique<int>(10);
// 无需手动 delete,离开作用域自动释放
总结:
| 情况 | 示例 | 风险 | 解决方法 |
|---|---|---|---|
| 未初始化的指针 | int* ptr; *ptr = 10; | 访问随机内存,崩溃 | 初始化时设为 nullptr |
| 指针越界 | int arr[5]; ptr[5] = 10; | 非法内存访问,数据损坏 | 检查边界,使用 STL 容器 |
| 内存释放后未置空 | delete ptr; *ptr = 10; | 访问已释放内存,UB | delete 后置空,改用智能指针 |
浙公网安备 33010602011771号