You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;
}

问题根源分析

  1. 未检查fread返回值:fread可能因读取错误或文件异常,实际读取的字节数小于length,此时buffer中从实际读取位置到length-1的区域是未初始化的垃圾值。strtok处理这些垃圾值时,可能会读到超出malloc分配的内存范围,触发Valgrind的invalid read错误。
  2. 未检查文件操作的错误状态:fseek、ftell都可能失败(比如读取无法seek的设备文件、空文件),导致length为无效值,后续malloc和字符串处理都会出错。
  3. 未处理空文件/全空格场景:如果文件内容全是空格或为空,count1会是0,此时创建的inputArray是长度为0的变长数组,后续赋值操作会直接越界。
  4. 未检查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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:57:56