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

出现Invalid read of size 4错误,求修复及fscanf避坑方法

Invalid read of size 4错误(fscanf调用处)的修复与避坑指南

问题代码

Queue* recordQueue = newQueue(NULL, NULL);
    
FILE* file = fopen(fileName, "r");
char* processName = malloc(8 * sizeof(char));
assert(processName != NULL);
unsigned int arriveTime;
unsigned int serviceTime;
short memoryRequirement;
while (fscanf(file, "%d %s %d %hd", &arriveTime, processName, 
    &serviceTime, &memoryRequirement) != EOF) {
    ProcessStatus status = NOT_READY;
    ProcessInfo* processInfo = newProcessInfo(processName, arriveTime, 
        serviceTime, memoryRequirement, status);
    
    processName = malloc(8 * sizeof(char));
    assert(processName != NULL);
    inQueue(recordQueue, newNode(processInfo, NULL, NULL));
}
return recordQueue;

Valgrind报错信息

==237688== Invalid read of size 4
==237688==    at 0x48C3AF2: __vfscanf_internal (vfscanf-internal.c:345)
==237688==    by 0x48C329C: __isoc99_fscanf (isoc99_fscanf.c:30)
==237688==    by 0x10A3AE: readProcessesFronFile (in /home/haozhec/project1/comp30023-2023-project-1/allocate)
==237688==    by 0x10A91D: main (in /home/haozhec/project1/comp30023-2023-project-1/allocate)
==237688==  Address 0xc0 is not stack'd, malloc'd or (recently) free'd
==237688== 
==237688== 
==237688== Process terminating with default action of signal 11 (SIGSEGV)
==237688==  Access not within mapped region at address 0xC0
==237688==    at 0x48C3AF2: __vfscanf_internal (vfscanf-internal.c:345)
==237688==    by 0x48C329C: __isoc99_fscanf (isoc99_fscanf.c:30)
==237688==    by 0x10A3AE: readProcessesFronFile (in /home/haozhec/project1/comp30023-2023-project-1/allocate)
==237688==    by 0x10A91D: main (in /home/haozhec/project1/comp30023-2023-project-1/allocate)

错误原因分析

  1. 文件指针未做有效性检查:如果fopen打开文件失败,file会返回NULL,将NULL传入fscanf会直接触发非法内存访问,这是本次SIGSEGV的核心诱因。
  2. 格式符与变量类型不匹配:arriveTime和serviceTime是unsigned int类型,但使用了对应signed int的%d格式符,会引发未定义行为,可能破坏内存布局。
  3. fscanf返回值判断逻辑错误:仅判断!=EOF无法处理部分字段读取失败的情况,会导致循环陷入死循环,进一步引发内存问题。
  4. 冗余动态内存分配与泄漏:每次循环都重新mallocprocessName,最后一次循环结束后,剩余的processName指针未释放,造成内存泄漏;同时频繁malloc增加了野指针风险。
  5. 未关闭文件:打开的文件未调用fclose,导致文件描述符资源泄漏。

修复后的代码

Queue* recordQueue = newQueue(NULL, NULL);

// 打开文件后立即检查有效性
FILE* file = fopen(fileName, "r");
if (!file) {
    fprintf(stderr, "无法打开文件: %s\n", fileName);
    // 必要时清理已分配资源后返回
    return NULL;
}

// 使用栈缓冲区替代动态分配,更安全高效
char processName[8];
unsigned int arriveTime;
unsigned int serviceTime;
short memoryRequirement;

// 判断是否成功读取全部4个字段,避免部分读取失败的死循环
while (fscanf(file, "%u %7s %u %hd", &arriveTime, processName, 
              &serviceTime, &memoryRequirement) == 4) {
    ProcessStatus status = NOT_READY;
    ProcessInfo* processInfo = newProcessInfo(processName, arriveTime, 
                                              serviceTime, memoryRequirement, status);
    
    inQueue(recordQueue, newNode(processInfo, NULL, NULL));
}

// 务必关闭文件,释放资源
fclose(file);
return recordQueue;

修复要点说明

  • 强制检查文件指针:文件打开失败是常见场景,必须处理该情况,避免空指针传入IO函数。
  • 格式符与变量严格对应:unsigned int对应%u,字符串读取用%7s限制长度(缓冲区8字节,留1字节存终止符),防止溢出。
  • 正确判断fscanf返回值:以成功读取的参数个数(此处为4)作为循环条件,确保每次读取都是完整有效的。
  • 用栈变量替代不必要的动态分配:减少内存泄漏和野指针风险,同时提升性能。
  • 及时释放资源:打开的文件必须调用fclose关闭,避免资源泄漏。

使用fscanf的避坑指南

  • 永远检查文件打开结果:只要涉及文件操作,第一步就是确认文件指针不为NULL。
  • 严格匹配格式符与类型:参考C标准文档,确保每个格式符对应正确的变量类型,尤其是无符号类型、短整型、长整型等特殊类型。
  • 限制字符串读取长度:使用%Ns格式符(N为缓冲区容量-1),彻底避免缓冲区溢出导致的内存破坏。
  • 不要依赖EOF判断循环:fscanf返回成功读取的字段数,用这个值判断是否完成一次有效读取,能避免部分读取失败的死循环。
  • 减少不必要的动态内存:栈内存比堆内存更安全,无需手动管理,适合固定大小的临时缓冲区。
  • 养成资源清理习惯:文件、动态内存等资源,在不再使用时必须及时释放,避免泄漏。

内容的提问来源于stack exchange,提问作者Howard Cui

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 01:22:03