Valgrind 测试详解--检测内存泄漏的好工具

一、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 lost

40 字节内存泄漏
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,分配和释放次数相等,没有内存泄漏。

相关推荐
uesowys2 小时前
Apache Spark算法开发指导-K-means
算法·spark·kmeans
你怎么知道我是队长2 小时前
C语言---排序算法10---基数排序法
算法
YGGP2 小时前
【Golang】LeetCode 56. 合并区间
算法·leetcode·职场和发展
_F_y2 小时前
回文串系列动态规划附马拉车算法原理及实现
算法·动态规划
你怎么知道我是队长2 小时前
C语言---排序算法12---计数排序法
c语言·算法·排序算法
fu的博客2 小时前
【数据结构2】带头结点·单向链表实现
数据结构·算法·链表
近津薪荼2 小时前
优选算法——前缀和(6):和可被 K 整除的子数组
c++·算法
海盗猫鸥2 小时前
Linux基础指令2
linux·c语言
白太岁2 小时前
通信:(2) TCP/UDP、流量/拥塞控制、ARP 与 Socket 应用
网络·c++·tcp/ip·udp