一、Valgrind 是什么?
Valgrind 是一个内存调试工具集,最常用的功能是 memcheck,可以检测:
- 内存泄漏 (allocated 但未 free)
- 非法内存访问(越界、使用未初始化内存)
- 重复释放 (double free)
Valgrind 常见输出
1:有内存泄漏
==1234== HEAP SUMMARY:
==1234== in use at exit: 100 bytes in 2 blocks
==1234== total heap usage: 5 allocs, 3 frees, 1,024 bytes allocated
==1234==
==1234== 100 bytes in 2 blocks are definitely lost in loss record 1 of 1
==1234== at 0x4846828: malloc (vg_replace_malloc.c:381)
==1234== by 0x1092AB: create_string_set_from_file (spellcheck.c:25)
==1234== by 0x1093CD: main (spellcheck.c:89)
- definitely lost : 确定的内存泄漏
- 显示了泄漏的位置(文件名、行号)
2:非法内存访问
==1234== Invalid read of size 1
==1234== at 0x1093AB: string_tolower (utilities.c:125)
==1234== Address 0x4a2c0d0 is 0 bytes after a block of size 10 alloc'd
==1234== at 0x4846828: malloc (vg_replace_malloc.c:381)
==1234== by 0x1092CD: create_string_set_from_file (spellcheck.c:42)
- 读取了未分配的内存(越界)
- 显示了非法地址和分配位置
3:使用未初始化值
==1234== Conditional jump or move depends on uninitialised value(s)
==1234== at 0x1093CD: binary_search (utilities.c:16)
二、内存泄漏的一个例子:
1.有内存泄漏的代码 leak.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 分配内存但忘记释放
void memory_leak_example() {
char *str = malloc(100); // 分配 100 字节
strcpy(str, "Hello, World!");
printf("String: %s\n", str);
// 忘记 free(str) - 内存泄漏!
}
// 分配内存后只在某个条件下释放
void conditional_leak(int flag) {
int *arr = malloc(10 * sizeof(int)); // 分配 40 字节(假设 int 4字节)
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
if (flag) {
printf("Flag is true, freeing memory\n");
free(arr); // flag=1 时释放
} else {
printf("Flag is false, NOT freeing memory\n");
// flag=0 时不释放 - 内存泄漏!
}
}
// 分配二维数组但释放不完全
void partial_free() {
// 分配 3 行,每行 5 个整数
int **matrix = malloc(3 * sizeof(int*));
for (int i = 0; i < 3; i++) {
matrix[i] = malloc(5 * sizeof(int));
for (int j = 0; j < 5; j++) {
matrix[i][j] = i * j;
}
}
// 只释放了指针数组,但没有释放每行的内存
free(matrix); // 每行的 malloc 泄漏!
// 正确做法:
// for (int i = 0; i < 3; i++) free(matrix[i]);
// free(matrix);
}
int main() {
printf("=== 内存泄漏示例 ===\n\n");
printf("1. 基本内存泄漏:\n");
memory_leak_example();
printf("\n2. 条件内存泄漏 (flag=0):\n");
conditional_leak(0);
printf("\n3. 部分释放:\n");
partial_free();
printf("\n程序结束\n");
return 0;
}
2.Makefile
CC = gcc
CFLAGS = -Wall -g
TARGET = leak
SRCS = leak.c
(TARGET): (SRCS)
(CC) (CFLAGS) -o @ ^
clean:
rm -f $(TARGET)
test: $(TARGET)
./$(TARGET)
valgrind: $(TARGET)
valgrind --leak-check=full ./$(TARGET)
valgrind-verbose: $(TARGET)
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./$(TARGET)
.PHONY: clean test valgrind valgrind-verbose
3. 终端指令
1. 编译程序
make clean
make
2. 正常运行(看不到内存问题)
./leak
3. 用 Valgrind 检测内存泄漏
valgrind --leak-check=full ./leak
4. 更详细的 Valgrind 输出
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./leak
4. Valgrind 报错信息
运行 valgrind --leak-check=full ./leak 后的输出:
==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==12345== Command: ./leak
==12345==
=== 内存泄漏示例 ===
1. 基本内存泄漏:String: Hello, World!
2. 条件内存泄漏 (flag=0):Flag is false, NOT freeing memory
3. 部分释放: 程序结束==12345==
==12345== HEAP SUMMARY:
==12345== in use at exit: 214 bytes in 5 blocks
==12345== total heap usage: 6 allocs, 1 frees, 1,238 bytes allocated
==12345==
==12345== 100 bytes in 1 blocks are definitely lost in loss record 1 of 3
==12345== at 0x4846828: malloc (vg_replace_malloc.c:381)
==12345== by 0x1091B4: memory_leak_example (leak.c:7)
==12345== by 0x1092E7: main (leak.c:50)
==12345==
==12345== 40 bytes in 1 blocks are definitely lost in loss record 2 of 3
==12345== at 0x4846828: malloc (vg_replace_malloc.c:381)
==12345== by 0x10920F: conditional_leak (leak.c:16)
==12345== by 0x1092F1: main (leak.c:53)
==12345==
==12345== 74 (24 direct, 50 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==12345== at 0x4846828: malloc (vg_replace_malloc.c:381)
==12345== by 0x10927A: partial_free (leak.c:31)
==12345== by 0x1092FB: main (leak.c:56)
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 164 bytes in 3 blocks
==12345== indirectly lost: 50 bytes in 2 blocks
==12345== possibly lost: 0 bytes in 0 blocks
==12345== still reachable: 0 bytes in 0 blocks
==12345== suppressed: 0 bytes in 0 blocks
==12345==
==12345== For lists of detected and suppressed errors, rerun with: -s
==12345== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
5. 报错信息解读
100 bytes in 1 blocks definitely lost
100 字节内存泄漏
leak.c:7 (memory_leak_example 中的 malloc)
40 bytes in 1 blocks definitely lost40 字节内存泄漏
leak.c:16 (conditional_leak 中的 malloc)
24 direct, 50 indirect直接泄漏 24 字节(指针数组),间接泄漏 50 字节(每行数据)
leak.c:31 (partial_free 中的外层 malloc)6 allocs, 1 frees 分配了 6 次,只释放了 1 次 5 次分配没释放
6. 修复后的正确代码
leak.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void memory_leak_example() {
char *str = malloc(100);
strcpy(str, "Hello, World!");
printf("String: %s\n", str);
free(str); // 修复:释放内存
}
void conditional_leak(int flag) {
int *arr = malloc(10 * sizeof(int));
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
if (flag) {
printf("Flag is true, freeing memory\n");
} else {
printf("Flag is false, still freeing memory\n");
}
free(arr); // 修复:无论 flag 值都释放
}
void partial_free() {
int **matrix = malloc(3 * sizeof(int*));
for (int i = 0; i < 3; i++) {
matrix[i] = malloc(5 * sizeof(int));
for (int j = 0; j < 5; j++) {
matrix[i][j] = i * j;
}
}
// 修复:先释放每行,再释放指针数组
for (int i = 0; i < 3; i++) {
free(matrix[i]);
}
free(matrix);
}
int main() {
printf("=== 修复后的版本 ===\n\n");
memory_leak_example();
conditional_leak(0);
partial_free();
printf("\n程序结束\n");
return 0;
}
7. 修复后的 Valgrind 输出
==54321== HEAP SUMMARY:
==54321== in use at exit: 0 bytes in 0 blocks
==54321== total heap usage: 6 allocs, 6 frees, 1,238 bytes allocated
==54321==
==54321== All heap blocks were freed -- no leaks are possible
==54321== ERROR SUMMARY: 0 errors from 0 contexts
关键变化:6 allocs, 6 frees,分配和释放次数相等,没有内存泄漏。