一、什么是内存泄漏?
在C语言中,我们可以使用malloc、calloc、realloc等函数来动态地申请内存空间,这些内存空间是从堆(heap)中分配的,与程序的生命周期无关,只有当我们显式地调用free函数来释放这些内存空间时,它们才会被回收。这样,我们就可以根据需要动态地调整内存的大小和数量,提高内存的利用率和程序的灵活性。
但是,这种动态内存分配的方式也带来了一些风险,就是如果我们在使用完动态分配的内存空间后,忘记或者无法释放它们,那么这些内存空间就会一直占用着系统的内存资源,无法被其他程序使用,这就是内存泄漏(memory leak)。
简单的内存泄漏示例代码:
1#include <stdlib.h>
2
3void allocateMemory() {
4 int* ptr = (int*)malloc(sizeof(int));
5 if (ptr == NULL) {
6 exit(1);
7 }
8 *ptr = 10;
9 // 注意:这里缺少了free(ptr)的调用,导致内存泄漏
10}
11
12int main() {
13 for (int i = 0; i < 10000; i++) {
14 allocateMemory();
15 }
16 return 0;
17}
上面的例子中,allocateMemory 函数使用 malloc 分配了一块内存,但之后并没有调用 free 释放这块内存。因此,每次调用 allocateMemory 函数时,都会有一块内存无法被释放,这就是内存泄漏。
二、内存泄漏的常见原因
1.内存泄漏的常见原因
(1)忘记释放内存
当使用malloc、calloc或realloc等函数分配内存后,必须在使用完内存后使用free来释放它。如果忘记释放,就会导致内存泄漏。
示例代码:
1#include <stdlib.h>
2
3void forgetToFree() {
4 int* ptr = (int*)malloc(sizeof(int));
5 if (ptr != NULL) {
6 *ptr = 10;
7 // 忘记调用 free(ptr)
8 }
9}
10
11int main(int argc, char *argv[]) {
12 for (int i = 0; i < 10000; i++) {
13 forgetToFree();
14 }
15 return 0;
16}
(2)重复释放
尝试多次释放同一块内存是非法的,可能导致程序崩溃。
示例代码:
1#include <stdlib.h>
2
3void doubleFree() {
4 int* ptr = (int*)malloc(sizeof(int));
5 if (ptr != NULL) {
6 free(ptr);
7 // 重复释放同一块内存
8 free(ptr);
9 }
10}
11
12int main(int argc, char *argv[]) {
13 doubleFree();
14 return 0;
15}
(3)内存泄漏在函数中
在函数内部分配的内存,如果没有被返回给调用者,调用者就无法释放它。
示例代码:
1#include <stdlib.h>
2
3void allocateInFunction() {
4 int* ptr = (int*)malloc(sizeof(int));
5 if (ptr != NULL) {
6 // ptr 没有返回给调用者,导致内存泄漏
7 *ptr = 10;
8 }
9}
10
11int main(int argc, char *argv[]) {
12 allocateInFunction();
13 // 无法释放 allocateInFunction 中分配的内存
14 return 0;
15}
(4)指针丢失
如果丢失了指向已分配内存的指针,那么这块内存就无法被释放。
示例代码:
1#include <stdlib.h>
2int* someOtherFunction() {
3 // 假设这个函数返回一个新的指针
4 int* newPtr = (int*)malloc(sizeof(int));
5 return newPtr;
6}
7
8void loseReference() {
9 int* ptr = (int*)malloc(sizeof(int));
10 if (ptr != NULL) {
11 *ptr = 10;
12 // 假设ptr被用于某个全局数据结构或传递给其他函数
13 // ...
14 // 然后ptr的引用丢失了,例如它指向的内存被另一个指针覆盖
15 // 假设这个函数返回一个新的指针
16 int* newPtr = someOtherFunction();
17 // 现在ptr指向了新的内存,原先的内存泄漏了
18 ptr = newPtr;
19 // 或者ptr可能被置为NULL,或者超出了其作用域
20 }
21}
22
23int main(int argc, char *argv[]) {
24 // 调用函数,ptr曾经指向一块内存
25 loseReference();
26 // 但由于在某处丢失了ptr的引用,这块内存无法被释放,导致内存泄漏
27 return 0;
28}
|