如何用Valgrind修复C语言read_lines_from_file函数的invalid write of size 8错误
Valgrind检测到read_lines_from_file函数的无效写入问题分析与修复
问题描述
Valgrind检测到如下错误:
invalid write of size 8 at read_lines_from_file.
原代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> int lines_count = 0; char** read_lines_from_file(FILE* file) { fseek(file, 0, SEEK_SET); char** lines = NULL;// ça renvoie NULL si le fichier est vide char line[100]; while (fgets(line, 100, file)) {// nous faisons l'hypothèse qu'une ligne ne dépasse pas 100 caractères line[strlen(line)-1]='\0';// on enlève le \n lines = realloc(lines, (lines_count + 1) * sizeof(char*)); lines[lines_count] = malloc(strlen(line) + 1); strcpy(lines[lines_count], line); lines_count++; } return lines; }
错误原因
全局变量
lines_count的设计缺陷
全局变量lines_count不会在函数调用时重置,多次调用函数时会延续之前的计数。比如第一次调用读取3行后lines_count=3,第二次调用时即使文件只有1行,代码会直接分配3+1个指针空间并写入lines[3],不仅浪费内存,还可能引发越界;若realloc失败返回NULL,后续对lines[lines_count]的赋值就是对NULL指针的非法访问,触发invalid write of size 8(64位系统下指针大小为8字节)。未检查内存分配返回值
realloc和malloc都可能因内存不足返回NULL,原代码直接使用返回值未做错误判断,一旦分配失败就会引发非法内存访问。换行符处理逻辑不严谨
盲目执行line[strlen(line)-1]='\0',若fgets读取的行没有换行符(比如文件最后一行、或行长度填满缓冲区且无换行),极端场景下strlen(line)-1会得到-1,导致数组下标越界,触发另一种无效写入。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 通过指针参数返回读取到的行数,避免全局变量污染 char** read_lines_from_file(FILE* file, int* out_line_count) { if (file == NULL || out_line_count == NULL) { return NULL; } // 检查文件指针重置是否成功 if (fseek(file, 0, SEEK_SET) != 0) { *out_line_count = 0; return NULL; } char** lines = NULL; char line[100]; int line_count = 0; while (fgets(line, sizeof(line), file)) { size_t len = strlen(line); // 仅当行尾是换行符时才替换为结束符,避免下标越界 if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } // 用临时变量接收realloc结果,避免分配失败丢失原内存 char** temp_lines = realloc(lines, (line_count + 1) * sizeof(char*)); if (temp_lines == NULL) { // 分配失败,释放已分配的所有内存 for (int i = 0; i < line_count; i++) { free(lines[i]); } free(lines); *out_line_count = 0; return NULL; } lines = temp_lines; // 分配单行内存并检查结果 lines[line_count] = malloc(len + 1); if (lines[line_count] == NULL) { // 分配失败,清理已分配内存 for (int i = 0; i < line_count; i++) { free(lines[i]); } free(lines); *out_line_count = 0; return NULL; } strcpy(lines[line_count], line); line_count++; } *out_line_count = line_count; return lines; }
修复要点
- 移除全局变量
lines_count,改用指针参数返回行数,避免多次调用的计数污染。 - 检查
fseek返回值,处理文件指针重置失败的情况。 - 用临时变量接收
realloc结果,避免分配失败时丢失原内存指针造成泄漏。 - 严谨处理换行符:仅当行尾确实是
\n时才替换,避免下标越界。 - 内存分配失败时,释放已分配的所有内存,避免内存泄漏。
- 使用
sizeof(line)代替硬编码的100,提升代码可维护性。
内容的提问来源于stack exchange,提问作者LesGEEKs456
相关产品推荐
相关产品推荐

