Valgrind未初始化值依赖问题:自制Unix grep工具调试求助
定位并修复Valgrind的"未初始化值"错误
先拆解下Valgrind的报错:错误出在printf("%s\n", line)调用时,printf内部依赖的strlen函数访问了未初始化的内存。问题核心有两个,咱们逐个说:
1. 未终止的字符串触发越界读取
你用malloc分配的line数组是未初始化状态的,读取字符时只填充了前last个字节,但剩下的size - last个字节还是垃圾值。虽然处理换行时会把整个line设为'\0',但:
- 如果文件最后一行没有以
'\n'结尾,循环不会进入换行处理分支,此时line没有字符串终止符'\0',printf("%s")会一直读取到内存中随机的'\0',途中必然碰到未初始化字节,触发Valgrind报错。 - 每次清空整个
line数组的做法效率很低,完全没必要。
2. 遗漏处理文件的最后一行
如果文件末尾没有换行符,最后一行的内容永远不会被传入check函数检查,这是个功能小bug。
修复方案
步骤1:确保字符串合法再传入check
不管是处理换行还是文件末尾,在把line传给check前,都要在line[last]位置添加'\0',把读取到的字符转换成标准C字符串。
步骤2:处理无换行的末尾行
在关闭文件前,检查last是否大于0(说明还有未处理的内容),如果是,就调用check处理这一行。
步骤3:优化内存重置逻辑
不需要清空整个line数组,只需要把last重置为0即可——后续写入会覆盖旧内容,且我们会在调用check前补全终止符。
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #define SIZE 100 int printed = 1; // return value -> 0 for pattern found, 1 for pattern not found char *pattern; char *dest; void check(char *line, int length, int size) { for (int i = 0; i < length; i++) { if (line[i] == pattern[0]) { int match = 1; for (int j = 1; j < size && (i+j) < length; j++) { if (line[i+j] != pattern[j]) { match = 0; break; } } if (match) { printf("%s\n", line); // print line printed = 0; // pattern found break; // 找到匹配后直接退出循环,无需继续检查 } } } } void read(void) { int c; int lengthPat = 0; // 计算pattern长度:微调逻辑更直观(原逻辑正确但易误解) while (pattern[lengthPat] != '\0') { lengthPat++; } FILE *file = fopen(dest, "r"); if (file == NULL) { fprintf(stderr, "Error: Could not open file!\n"); return; } size_t size = 100; char *line = malloc(size * sizeof(char)); if (line == NULL) { fclose(file); // 修复:打开文件后malloc失败,先关文件再退出 exit(102); } int last = 0; while ((c = getc(file)) != EOF) { if (c != '\n') { // 读取字符直到换行 if (last == size) { char *p_line = realloc(line, 2 * size * sizeof(char)); if (p_line == NULL) { free(line); fclose(file); exit(102); } line = p_line; size *= 2; } line[last++] = (char)c; } else { // 处理换行,检查当前行 line[last] = '\0'; // 添加字符串终止符 check(line, last, lengthPat); last = 0; // 重置索引,无需清空整个数组 } } // 处理文件末尾无换行的最后一行 if (last > 0) { line[last] = '\0'; check(line, last, lengthPat); } fclose(file); free(line); } /* The main program */ int main(int argc, char *argv[]) { if (argc == 3) { pattern = argv[1]; dest = argv[2]; read(); } return printed; }
额外小优化
- 把原
check函数的goto换成了标志变量+break,代码可读性更好。 - 修复了内存分配失败时的资源泄漏:先关闭已打开的文件再退出。
- 微调了
pattern长度计算的循环逻辑,更直观易懂。
现在用Valgrind重新检测,"未初始化值"的错误应该会消失,同时也修复了最后一行不处理的功能bug。
内容的提问来源于stack exchange,提问作者LAKY911
相关产品推荐
相关产品推荐

