简易Shell程序Valgrind内存泄漏与invalid free()问题求助
简易Shell的内存泄漏与无效free问题排查
我自己写了个简易Shell,输入解析和命令执行功能正常,但Valgrind检测出内存泄漏和invalid free()错误,怎么都找不到问题,求帮忙。
Valgrind检测结果
#cisfun$ /bin/ls -l token: /bin/ls arg_list[0]: /bin/ls token: -l arg_list[1]: -l token: (null) cmd: /bin/ls total 204 -rwxrw-r-- 1 vagrant vagrant 104 Jul 2 12:39 AUTHORS -rwxrw-r-- 1 vagrant vagrant 23 Jun 14 16:25 README.md -rw-rw-r-- 1 vagrant vagrant 575 Jul 4 08:25 execute.c -rw-rw-r-- 1 vagrant vagrant 197 Aug 13 09:17 free_arg.c -rwxr-xr-x 1 vagrant vagrant 142144 Jul 3 05:04 hbtn_ls -rw-rw-r-- 1 vagrant vagrant 810 Aug 13 09:15 parse_arg.c -rwxrw-r-- 1 vagrant vagrant 789 Jul 4 12:05 puts.c -rwxrwxr-x 1 vagrant vagrant 27888 Aug 13 09:15 shell -rwxrw-r-- 1 vagrant vagrant 1186 Aug 13 10:47 shell.c -rwxrw-r-- 1 vagrant vagrant 398 Jul 4 10:29 shell.h -rw-rw-r-- 1 vagrant vagrant 156 Jul 4 08:31 sig_handler.c #cisfun$ ^Z ==8999== Invalid free() / delete / delete[] / realloc() ==8999== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==8999== by 0x109423: free_arg (free_arg.c:12) ==8999== by 0x10991C: main (shell.c:77) ==8999== Address 0x4a4f040 is 0 bytes inside a block of size 120 free'd ==8999== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==8999== by 0x10990D: main (shell.c:76) ==8999== Block was alloc'd at ==8999== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==8999== by 0x48DD573: getdelim (iogetdelim.c:62) ==8999== by 0x1097EB: main (shell.c:47) ==8999== ==8999== ==8999== HEAP SUMMARY: ==8999== in use at exit: 11 bytes in 2 blocks ==8999== total heap usage: 8 allocs, 8 frees, 2,227 bytes allocated ==8999== ==8999== 11 bytes in 2 blocks are definitely lost in loss record 1 of 1 ==8999== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==8999== by 0x109528: parse_arg (parse_arg.c:29) ==8999== by 0x109843: main (shell.c:54) ==8999== ==8999== LEAK SUMMARY: ==8999== definitely lost: 11 bytes in 2 blocks ==8999== indirectly lost: 0 bytes in 0 blocks ==8999== possibly lost: 0 bytes in 0 blocks ==8999== still reachable: 0 bytes in 0 blocks ==8999== suppressed: 0 bytes in 0 blocks ==8999== ==8999== For lists of detected and suppressed errors, rerun with: -s ==8999== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
相关代码片段
main函数(shell.c)
47 nread = getline(&line, &len, stdin); 48 if (nread == -1) 49 break; 50 51 if (line[nread - 1] == '\n') 52 line[nread - 1] = '\0'; 53 54 arg_list = parse_arg(line); 55 56 cmd = arg_list[0]; 57 printf("cmd: %s\n", cmd); 58 if (cmd == NULL || *cmd == '\0') 59 continue; 60 61 62 63 if (stat(cmd, &st) == 0) 64 { 65 execute(arg_list, prog_name); 66 } 67 else 68 { 69 perror(prog_name); 70 } 71 } 72 73 if (isatty(STDIN_FILENO)) 74 _puts("\n"); 75 76 free(line); 77 free_arg(arg_list); 78 return (0);
parse_arg函数(parse_arg.c)
7 char **parse_arg(char *arg) 8 { 9 char **arg_list; 10 char *str, *token; 11 int i, j = 1; 12 13 arg_list = malloc(sizeof(*arg_list) * j); 14 if (arg_list == NULL) 15 return (NULL); 16 17 for (i = 0, str = arg; ;str = NULL, i++) 18 { 19 token = strtok(str, " "); 20 printf("token: %s\n", token); 21 22 if (token == NULL) 23 { 24 arg_list[i] = NULL; 25 break; 26 } 27 else 28 { 29 arg_list[i] = malloc(sizeof(char) * strlen(token) + 1); 30 if (arg_list[i] == NULL) 31 { 32 while (i--) 33 free(arg_list[i]); 34 35 free(arg_list); 36 return (NULL); 37 } 38 arg_list[i] = token; 39 printf("arg_list[%d]: %s\n", i, arg_list[i]); 40 } 41 arg_list = realloc(arg_list, sizeof(*arg_list) * ++j); 42 if (arg_list == NULL) 43 return (NULL); 44 45 } 46 return (arg_list); 47 }
free_arg函数(free_arg.c)
6 void free_arg(char **arg) 7 { 8 int i; 9 10 for (i = 0; arg[i] != NULL; i++) 11 { 12 free(arg[i]); 13 } 14 free(arg); 15 }
问题分析与修复方案
核心问题
- 无效free()错误:
strtok返回的指针指向原始输入字符串line的内部区域,不属于堆内存。但free_arg里尝试free这些指针,而line已经在main函数中被free,导致重复释放无效内存。 - 内存泄漏:
parse_arg第29行为arg_list[i]分配了堆内存,但第38行直接用token覆盖了该指针,导致分配的内存地址丢失,无法被释放。
修复方法
修复parse_arg函数
将第38行的直接赋值改为字符串拷贝,确保arg_list[i]指向堆上的独立内存:
方法一:使用strcpy(保留原malloc逻辑)
// 替换原第28-39行代码 else { arg_list[i] = malloc(sizeof(char) * (strlen(token) + 1)); if (arg_list[i] == NULL) { while (i--) free(arg_list[i]); free(arg_list); return (NULL); } strcpy(arg_list[i], token); // 拷贝token内容到堆内存 printf("arg_list[%d]: %s\n", i, arg_list[i]); }
方法二:使用strdup(自动分配内存并拷贝,更简洁)
// 替换原第28-39行代码 else { arg_list[i] = strdup(token); // strdup会自动malloc并拷贝字符串 if (arg_list[i] == NULL) { while (i--) free(arg_list[i]); free(arg_list); return (NULL); } printf("arg_list[%d]: %s\n", i, arg_list[i]); }
使用方法二时,需删除原第29行的malloc语句,避免重复分配内存。
验证修复效果
修复后,arg_list[i]均指向独立的堆内存,free_arg中的free操作会正确释放这些内存;同时不再出现无效free的情况,内存泄漏也会被解决。
内容的提问来源于stack exchange,提问作者Bayeroabdul
相关产品推荐
相关产品推荐

