一、什么是内存泄漏?
在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}
|