同一文件混合调用fgetc、ungetc与fputc时出错的原因排查
混合使用fputc()与ungetc()引发文件操作错误的原因
问题重现
测试代码
#include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; int c; fp = fopen("filetest.txt", "r+"); int fno = fileno(fp); /* 读取并打印第一个字符'1' */ if((c = fgetc(fp)) == EOF){ printf("fgetc(): error\n"); }else{ printf("%c\n", c); } /* 读取第二个字符'2' */ if((c = fgetc(fp)) == EOF){ printf("fgetc(): error\n"); } /* 在第三个位置写入字符'X' */ if((fputc('X', fp)) == EOF){ printf("fputc(): error\n"); } /* -- 问题代码段 --- */ /* 把字符'2'退回输入流 */ if (ungetc(c, fp) == EOF){ printf("ungetc(): error\n"); } /* 再次读取并打印'2' */ if((c = fgetc(fp)) == EOF){ printf("fgetc(): error\n"); }else{ printf("%c\n", c); } /* -- 问题代码段结束 --- */ /* 读取第四个字符'4' */ if((c = fgetc(fp)) == EOF){ printf("fgetc(): error\n"); }else{ printf("%c\n", c); } if (fclose(fp) != 0){ printf("fclose(): error\n"); } }
测试文件内容
filetest.txt初始内容:
12345
预期输出
1 2 4
实际输出
1 2 fgetc(): error fclose(): error
对比现象
移除标注为“问题代码段”的部分后,输出恢复正常:
1 4
错误原因
核心问题在于标准IO流的读写切换规则:
当以r+(读写模式)打开文件时,从读操作切换到写操作,或者从写操作切换到读操作,必须满足以下条件之一,否则行为是未定义的:
- 插入
fflush()调用刷新输出缓冲区 - 调用
fseek()、fsetpos()或rewind()调整文件位置指针(哪怕只是定位到当前位置)
你的代码执行流程中:
- 先执行两次
fgetc(读操作),文件指针停在原字符'3'的位置 - 调用
fputc写入'X',完成写操作,此时流处于写状态 - 直接调用
ungetc(属于读操作范畴,用于把字符退回输入流供后续读操作获取),没有做任何流同步/定位操作,违反了读写切换规则,触发未定义行为 - 后续的
fgetc和fclose都会因为流的状态异常而失败
修复方案
在fputc之后、ungetc之前,插入一个流同步操作,比如调用fseek定位到当前位置(不改变指针位置,仅触发流状态切换):
/* 在fputc之后添加 */ fseek(fp, 0, SEEK_CUR);
或者调用fflush刷新输出缓冲区:
fflush(fp);
这样就能合法地从写操作切换回读操作,ungetc和后续的fgetc都能正常工作。
内容的提问来源于stack exchange,提问作者MrIo
相关产品推荐
相关产品推荐

