使用strtok处理文件内容时触发Valgrind的invalid read size 1错误排查
Valgrind检测到
invalid read size 1错误的原因及修复 我将文本文件读取为char数组,使用strtok以空格为分隔符拆分内容,把每个拆分出的单词存入inputArray的子数组中。程序在CLion中运行正常,但通过命令行编译后,Valgrind检测出‘invalid read size 1’错误。我了解strtok要求字符串以空字符结尾,且自认为已实现该处理,请问问题出在哪里?
原代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> char *read_string(const char *filename) { char *buffer = 0; long length; FILE *f = fopen(filename, "rb"); //was "rb" if (f) { fseek(f, 0, SEEK_END); length = ftell(f); fseek (f, 0, SEEK_SET); buffer = (char *)malloc((length + 1) * sizeof(char)); if (buffer) { fread(buffer, sizeof(char), length, f); buffer[length] = '\0'; } fclose(f); } return buffer; } int main() { char *inputFile = read_string("input file.txt"); char *inputFile3 = read_string("input file.txt"); // counts the number of characters in the file to determine size of array to create int count1 = 0; for (char *p = strtok(inputFile, " "); p != NULL; p = strtok(NULL, " ")) { count1 += 1; } char *inputArray[count1]; // creating array of words int l = 0; for (char *p = strtok(inputFile3, " "); p != NULL; p = strtok(NULL, " ")) { inputArray[l] = p; l++; } return 0; }
问题根源分析
- 未检查
fread返回值:fread可能因读取错误或文件异常,实际读取的字节数小于length,此时buffer中从实际读取位置到length-1的区域是未初始化的垃圾值。strtok处理这些垃圾值时,可能会读到超出malloc分配的内存范围,触发Valgrind的invalid read错误。 - 未检查文件操作的错误状态:
fseek、ftell都可能失败(比如读取无法seek的设备文件、空文件),导致length为无效值,后续malloc和字符串处理都会出错。 - 未处理空文件/全空格场景:如果文件内容全是空格或为空,
count1会是0,此时创建的inputArray是长度为0的变长数组,后续赋值操作会直接越界。 - 未检查
read_string返回值:如果文件打开失败,inputFile和inputFile3会是NULL,strtok处理NULL指针会触发未定义行为,Valgrind也会报错。
修复方案
针对上述问题,逐一修正代码:
- 增加所有文件操作、内存分配的错误检查;
- 确保
fread未读满时,将实际读取位置的下一位设为\0,避免处理未初始化内存; - 处理空文件/无有效单词的场景;
- 释放已分配的内存,避免内存泄漏。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> char *read_string(const char *filename) { char *buffer = NULL; long length; FILE *f = fopen(filename, "r"); // 读取文本文件建议用文本模式,而非二进制模式 if (!f) { perror("文件打开失败"); return NULL; } if (fseek(f, 0, SEEK_END) != 0) { perror("定位到文件末尾失败"); fclose(f); return NULL; } length = ftell(f); if (length == -1L) { perror("获取文件长度失败"); fclose(f); return NULL; } if (fseek(f, 0, SEEK_SET) != 0) { perror("定位到文件开头失败"); fclose(f); return NULL; } buffer = malloc((length + 1) * sizeof(char)); if (!buffer) { perror("内存分配失败"); fclose(f); return NULL; } size_t bytes_read = fread(buffer, sizeof(char), length, f); if (bytes_read != (size_t)length) { fprintf(stderr, "警告:仅读取了%zu字节,预期%ld字节\n", bytes_read, length); buffer[bytes_read] = '\0'; } else { buffer[length] = '\0'; } fclose(f); return buffer; } int main() { char *inputFile = read_string("input file.txt"); char *inputFile3 = read_string("input file.txt"); if (!inputFile || !inputFile3) { fprintf(stderr, "读取输入文件失败\n"); free(inputFile); free(inputFile3); return EXIT_FAILURE; } int count1 = 0; for (char *p = strtok(inputFile, " "); p != NULL; p = strtok(NULL, " ")) { count1 += 1; } if (count1 == 0) { printf("文件中未找到有效单词\n"); free(inputFile); free(inputFile3); return EXIT_SUCCESS; } char *inputArray[count1]; int l = 0; for (char *p = strtok(inputFile3, " "); p != NULL; p = strtok(NULL, " ")) { inputArray[l] = p; l++; } // 示例:打印所有拆分出的单词 for (int i = 0; i < count1; i++) { printf("单词%d:%s\n", i+1, inputArray[i]); } free(inputFile); free(inputFile3); return EXIT_SUCCESS; }
内容的提问来源于stack exchange,提问作者ben
相关产品推荐
相关产品推荐

