C语言中fgetc与&&、||运算符的正确使用问询
嘿,我来帮你拆解这些fgetc和&&、||运算符搭配时的坑,这些都是C文件操作里很容易踩的点!
1. 两次调用fgetc时会给ch赋值两次吗?
当然会!而且这正是你最初输出异常的核心原因。举个例子,假设你写了这样的循环条件:
char ch; // 注意:这里应该用int,后面会说原因 while ((ch = fgetc(fp1)) != EOF && (ch = fgetc(fp2)) != EOF) { putchar(ch); }
这里的&&是短路求值:先执行左边的ch = fgetc(fp1),判断是否不等于EOF。如果成立,才会执行右边的ch = fgetc(fp2)——这时候ch会被fp2的字符覆盖,而且两次fgetc调用会分别消耗fp1和fp2的一个字符,等于你每次循环跳过了fp1的一个字符,只输出fp2的内容,自然会出现输出异常。
如果是||的情况,比如:
while ((ch = fgetc(fp1)) != EOF || (ch = fgetc(fp2)) != EOF) { putchar(ch); }
短路求值同样生效:左边为真时(fp1没到EOF),右边的fgetc(fp2)完全不会执行,所以你会先把fp1的所有字符读完,才开始读fp2的字符。而如果ch是char类型,当读到EOF时,-1会被转成无符号的255,输出就是你看到的“垃圾字符”。
2. 如何正确结合&&运算符使用fgetc?
&&的逻辑是“两个条件都满足才继续循环”,对应到合并文件的场景,应该是每次循环同时读取两个文件的一个字符,直到其中一个文件到EOF,之后再处理剩下的字符。
正确的写法需要用两个变量分别存储两个文件的字符,避免覆盖,同时必须把变量定义为int(因为fgetc返回int类型,包含EOF的-1,用char会丢失EOF的判断):
#include <stdio.h> int main() { FILE *fp1 = fopen("file1.txt", "r"); FILE *fp2 = fopen("file2.txt", "r"); if (!fp1 || !fp2) { perror("Failed to open file"); return 1; } int ch1, ch2; // 同时读两个文件,直到其中一个到EOF while ((ch1 = fgetc(fp1)) != EOF && (ch2 = fgetc(fp2)) != EOF) { putchar(ch1); putchar(ch2); } // 处理fp1剩余的字符 while ((ch1 = fgetc(fp1)) != EOF) { putchar(ch1); } // 处理fp2剩余的字符 while ((ch2 = fgetc(fp2)) != EOF) { putchar(ch2); } fclose(fp1); fclose(fp2); return 0; }
这里的逻辑很清晰:先交替读取两个文件的字符,当其中一个文件读完后,再把另一个文件剩下的内容全部输出。
3. 如何正确结合||运算符使用fgetc?
||的逻辑是“只要有一个条件满足就继续循环”,对应到合并文件的场景,应该是只要任意一个文件还有字符,就继续读取输出(比如交替读取直到两个都读完,或者先读完第一个再读第二个)。
先解决你遇到的两个问题:
- 两次调用fgetc出现垃圾字符:大概率是因为你把
ch定义成了char,而不是int。EOF是-1,当用无符号char存储时会变成255,输出就是乱码,所以必须用int来接收fgetc的返回值。 - 单次调用出现无限循环:应该是你的写法错误,比如没有正确标记文件是否读完,导致重复读取EOF进入死循环。
正确的写法可以用标记位跟踪每个文件的状态,避免短路求值导致的漏读:
#include <stdio.h> int main() { FILE *fp1 = fopen("file1.txt", "r"); FILE *fp2 = fopen("file2.txt", "r"); if (!fp1 || !fp2) { perror("Failed to open file"); return 1; } int ch; int fp1_eof = 0, fp2_eof = 0; // 只要有一个文件没读完就继续循环 while (!fp1_eof || !fp2_eof) { if (!fp1_eof) { ch = fgetc(fp1); if (ch == EOF) { fp1_eof = 1; } else { putchar(ch); } } if (!fp2_eof) { ch = fgetc(fp2); if (ch == EOF) { fp2_eof = 1; } else { putchar(ch); } } } fclose(fp1); fclose(fp2); return 0; }
如果你想要的是“先读完第一个文件,再读第二个”,那逻辑更简单,直接用两个独立的while循环即可,本质上也是||的逻辑:
// 先读file1 int ch; while ((ch = fgetc(fp1)) != EOF) { putchar(ch); } // 再读file2 while ((ch = fgetc(fp2)) != EOF) { putchar(ch); }
这种写法完全避免了逻辑运算符和fgetc结合的坑,适合简单的顺序合并场景。
内容的提问来源于stack exchange,提问作者Ammar Brk

