为何while ((c = getc(file)) != EOF)读文件在部分平台可正常运行?
为什么这段文件读取代码在部分平台正常工作,部分平台陷入死循环?
有讨论指出以下代码读取文件可能陷入死循环,原因是EOF是超出char范围的整数,导致while条件无法成立:
FILE* f; char c; std::string s; f = fopen("/some/file", "r"); while ((c = getc(f)) != EOF) s += c;
实际测试发现,这段代码在树莓派Linux系统下会陷入死循环,但在其他硬件的Linux系统下可正常运行。提问者猜测赋值语句(c = getc(f))的返回值定义不明确,部分平台返回左值(char类型),部分平台返回右值(int类型),想了解具体原因。
核心原因:char的符号属性与整数提升规则
你的猜测并不准确,C标准对赋值表达式的返回值规则是明确的:赋值表达式c = getc(f)返回的是左值c转换后的右值,类型为char。问题的根源不在赋值返回值的定义,而在于两个关键细节:
getc的返回值规定:C标准强制要求getc()返回int类型。它会将读取到的字符以unsigned char的形式转换为int(取值范围0~255),若到达文件末尾或读取出错,则返回EOF(通常定义为-1,一个负整数)。char的符号属性依赖平台:C标准未规定char是有符号还是无符号,这由编译器/平台决定:- 若平台默认
char为无符号类型:当getc()返回EOF(-1)时,赋值给unsigned char c会将-1转换为255(无符号char的最大值)。此时赋值表达式的结果会被整数提升为int类型的255,与EOF(-1)比较永远不相等,循环陷入死循环。 - 若平台默认
char为有符号类型:当getc()返回EOF(-1)时,赋值给signed char c刚好能保存-1。赋值表达式结果被整数提升为int类型的-1,与EOF相等,循环正常终止。
- 若平台默认
树莓派触发死循环的原因
树莓派采用的ARM架构默认char为无符号类型,而多数x86/x86_64架构的Linux系统默认char为有符号类型,这就是代码跨平台表现不一致的核心原因。
正确的修复写法
要彻底避免这个问题,必须将c声明为int类型,确保能容纳getc()返回的所有可能值(包括EOF):
FILE* f; int c; // 改为int类型 std::string s; f = fopen("/some/file", "r"); if (f == NULL) { // 处理文件打开失败的情况 } while ((c = getc(f)) != EOF) s += (char)c; // 转换为char后再追加到字符串
内容的提问来源于stack exchange,提问作者cdalitz
相关产品推荐
相关产品推荐

