内存溢出&内存泄漏 (C语言)
写在前面:
c语言中常见的内存管理问题,代码检查非常困难,众多程序Bug均来自它们,指针功能非常强大,但是程序可读性较差,对新手极其不友好。但是如果你掌握空指针 野指针和指针运算,知道二级指针 指针数组 数组指针请跳过。使用指针有很多不可控事件发生,如硬件开发中极容易出现烧毁电路甚至炸电容的事情发生,所以能不用指针就尽量不要用。
尽量 不要 不要 不要 不要 使用 指针!!!
1.内存泄漏(Memory Leak)
动态内存分配后未能正确释放内存,导致内存资源无法被回收利用,随着程序运行,为释放的内存堆积,最终导致内存耗尽,性能下降和程序崩溃。
内存泄漏主要原因:
1.1.指针重新赋值:
void test(int* ps){
ps = 10; //此时ps就是一个指针
}
1.2.错误的内存释放
- 栈的错误内存示范
/*数组方式的结构,不释放ps,而是释放数组中动态开辟空间的区域ps->a
*/
typedef int STDataType;
typedef struct Stack{
int top; //栈顶 数据个数
int capacity; //空间
STDataType* a; //数据容器
}ST;
/*销毁栈 示例*/
void StackDestory(ST* ps){
assert(ps);
ps->top = 0;
ps->capacity = 0;
// free(ps); //错误的内存释放
free(ps->a); //示例~~~~~~~~~~~~~~~~~~~~
ps->a = NULL; //防御性编程,指针置空,同时无法再访问导致野指针出现
}
- 队列的错误内存释放
//数据存储结构
typedef struct QueueNode{
QDataType data;
struct QueueNode* next;
}QNode;
//队头指针和队尾指针
typedef struct Queue{
QNode* head; //队尾
QNode* tail; //队尾
}QUeue;
//示例
void QueueDestory(QUeue* pq){
assert(pq);
while (pq->head != NULL){
QNode* temp = pq->head; //让temp指向第一个节点
pq->head = pq->head->next; //防止队头指针释放,让头指针指向下一个指针
free(temp);
}
pq->head = pq->tail = NULL; //队头队尾指针置空
}
1.3.返回值的不正确处理
int text(int* a){
a = 20;
}
//main函数
int as = 10;
test(&as); //int* ab = test(&as); 返回值的不正确处理
导致的后果:
- 系统性能下降
- 可用内存被耗尽
- 程序崩溃,特别是在长时间运行的程序中(如服务器)
2.内存溢出(Memory Overflow)
2.1数组越界和指针错误
int arr[2]; //0 1
for (int i = 0; i < 3; i++){ //0 1 2
arr[i] = i;
}
2.2栈溢出
void stackOverflow() {
int array[10000]; // 分配一个大数组,消耗栈内存
stackOverflow(); // 递归调用
}
int main() {
stackOverflow(); // 触发栈溢出
return 0;
}
2.3堆溢出
int main() {
while (1) {
// 不断分配大量内存,导致堆溢出
int *ptr = (int *)malloc(100000000 * sizeof(int));
if (ptr == NULL) {
exit(-1);
break;
}
}
return 0;
}
2.4缓冲区溢出 -数组越界
void bufferOverflow() {
char buffer[10];
for (int i = 0; i <= 10; i++) { // 越界写入 buffer[10]
buffer[i] = 'a'; // 访问超出边界的内存
}
}
int main() {
bufferOverflow(); // 触发缓冲区溢出
return 0;
}
导致的后果:
- 数据损坏
- 安全漏洞
- 系统不稳定
- 程序崩溃
tip: 销毁指针时候将指针置空是一种防御性编程,首先防止误用已经被释放的内存,其次提高了代码的健壮性。
3. 区别
特性 | 内存溢出(Memory Overflow) | 内存泄漏(Memory Leak) |
---|---|---|
定义 | 访问超出分配的内存范围 | 动态分配的内存没有被释放 |
常见原因 | 数组越界、指针错误、未检查边界条件 | 使用 malloc 等动态分配后,未使用 free 释放 |
表现形式 | 程序崩溃、数据损坏、未定义行为、系统异常 | 内存被逐渐耗尽,程序运行缓慢,内存耗尽导致崩溃 |
常见影响 | 程序可能立即崩溃或出现错误行为 | 随着时间推移,内存会逐渐消耗,导致系统资源不足 |
解决方法 | 避免数组越界、使用有效的边界检查 | 在使用完动态内存后,确保调用 free 释放内存 |
4.防止内存溢出和内存泄漏的建议
防止内存溢出:
- 边界检查
- 使用调试工具
- 初始化指针
防止内存泄漏:
- 动态内存管理
- 跟踪内存分配
- 工具检测