欢迎拜访:雾里看山-CSDN博客
本篇主题:C语言动态内存管理(下)
发布时间:2025.3.18
隶属专栏:C语言
目录
- 动态内存常见错误
- 内存泄漏(Memory Leak)
- 典型场景
- 后果
- 解决方案
- 悬挂指针(Dangling Pointer)
- 错误示例
- 深层原理
- 修复方案
- 双重释放(Double Free)
- 危险操作
- 系统表现
- 防护措施:
- 越界访问(Out-of-Bounds)
- 典型错误
- 内存影响
- 未初始化访问
- 问题代码
- 随机性危害
- 防御性编程
- 释放栈区内容
- 错误示例
- 解决方案
- 未全部释放
- 错误示例
- 解决方案
- C/C++程序的内存开辟
动态内存常见错误
内存泄漏(Memory Leak)
典型场景
void process_data() {int* buffer = malloc(1024 * sizeof(int));// 忘记调用 free(buffer)
}
后果
- 程序持续运行时会不断消耗内存
- 长期运行的服务可能因此崩溃
解决方案
- 遵循申请与释放成对出现原则
- 使用RAII模式(C可用
__attribute__((cleanup))
扩展)
悬挂指针(Dangling Pointer)
错误示例
int* create_int() {int value = 42;return &value; // 返回局部变量地址
}int main() {int* ptr = create_int();printf("%d", *ptr); // 不可预测结果
}
深层原理
- 栈帧销毁后,原地址可能被其他数据覆盖
- 堆内存释放后若未置空,指针仍保存失效地址
修复方案
及时释放,释放后立即置空
free(ptr);
ptr = NULL; // 立即置空
双重释放(Double Free)
危险操作
对同一地址空间进行多次释放
char* str = malloc(64);
free(str);
free(str);
系统表现
- 可能立即引发
segmentation fault
- 可能破坏堆管理结构导致后续
malloc
失败
防护措施:
- 使用释放后置空的编程规范
- 在复杂逻辑中明确资源所有权
越界访问(Out-of-Bounds)
典型错误
数组只开辟了[0,4]
的空间,访问时却访问了下标为5的空间。
int* arr = malloc(5 * sizeof(int));
for(int i=0; i<=5; i++) { // 索引0-4有效arr[i] = i; // i=5时越界
}
内存影响
- 可能覆盖相邻内存的控制信息
- 可能修改其他变量值导致逻辑错误
未初始化访问
问题代码
内存开辟出来未进行初始化就直接访问。
int* p = malloc(sizeof(int));
printf("%d", *p);
随机性危害
- 可能意外修改关键内存区域
- 在不同运行环境中表现不一致
防御性编程
指针定义时显示初始化, 指针使用时进判空。
int* p = malloc(sizeof(int));
*p = 10;
printf("%d", *p);
释放栈区内容
错误示例
对于非动态开辟出来的内存进行释放
void test()
{int a = 10;int *p = &a;free(p);
}
解决方案
- 遵循申请与释放成对出现原则
- 使用RAII模式(C可用
__attribute__((cleanup))
扩展)
未全部释放
错误示例
在释放内存的时候,创建时的指针和原来的指针已经不在同一个位置,直接释放则会有内存错误。
void test()
{int *p = (int *)malloc(100);p++;free(p);
}
解决方案
在创建的时候,使用一个指针记录起始位置,
void test()
{int *p = (int *)malloc(100);int *ptr=p;p++;free(ptr);
}
C/C++程序的内存开辟
C/C++程序内存分配的几个区域:
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
- 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
- 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但是被static
修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长。
⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!