嘉兴市网站建设_网站建设公司_Tailwind CSS_seo优化
2025/12/18 0:23:13 网站建设 项目流程

TL;DR: 应该用 int类型的变量接收 fgetc() 的返回值

fgetc(3):

fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.


原来的程序要读的文件内容只有一个字符。(ch = fgetc(fp)) != EOF 却一直是 true。程序中有一段是这样的:

while ((ch = fgetc(fp)) != EOF) {}

【这里应该有一张 gif 图片】

产生了一个死循环 (故意隐藏 ch 的定义)。

熟悉 C 语言 getchar() 系列函数的一眼就能猜到,ch 的类型肯定是 char 导致了这个问题。

getchar(), fgetc() 系列函数的返回值是 intch的类型应该为int

K&R p.18

The problem is distinguishing the end of input from valid data. The solution is that getchar

returns a distinctive value when there is no more input, a value that cannot be confused with

any real character. This value is called EOF, for ``end of file''. We must declare c to be a type big enough to hold any value that getchar returns. We can't use char since c must be big enough to hold EOF in addition to any possible char. Therefore we use int.

最小复现示例

下面是一个名为 read-null 的程序的代码,这个程序在 x86_64 不会死循环,但是在 aarch64 上会死循环。

#include <stdio.h>
#include <assert.h>int main()
{FILE *fp = fopen("/dev/null", "r");assert(fp != NULL);char ch;while ((ch = fgetc(fp)) != EOF) { // Reads from /dev/null always return end of fileprintf("Infinite loop...\n");}fclose(fp);return 0;
}

下面是这个示例在 x86_64 的运行效果,程序直接读到 EOF 退出了:

aarch64 gcc 把比较指令都优化掉了

在 aarch64 机器上,b .L5,无条件跳转,编译器知道 charint-1 比较,永远都是 true,直接给优化成死循环了,都_没有对比 _(char)fgetc()_ 返回值是否不等于 _EOF_ 这一条指令_。

判断 EOF 的操作被编译器优化了。

为什么 x86_64 上是正常的?

C 语言的 EOF一般是常量 -1,来看看如果使用 char 类型的变量来接收 fgetc()的返回值,char-1int-1 到底相等不相等。

#include <stdio.h>
#include <assert.h>int main()
{
#if defined(__x86_64__) || defined(_M_X64)printf("=====On x86_64=====\n");
#elif defined(__aarch64__)printf("=====On ARM64=====\n");
#endifassert((unsigned char)-1 != -1);printf("(unsigned char)-1 != -1 is true\n");assert((char)-1 != -1);printf("(char)-1 != -1 is true\n");return 0;
}

x86_64 上,(char)-1 != -1false。aarch64 上,(char)-1 != -1true

image
image

char 和 int 做比较,char 会被隐式转换为 intunsigned char展开到 int 的时候符号位是不会扩展的,换句话说,(unsigned char)-1 转为 int是 255。(char)-1int 会是多少呢?

x86_64 上是 -1,arm64 上是 255. char 的负数转 int ,或者说 signed char 转 int 这是 UB

K&R p.41

On some machines a char whose leftmost bit

is 1 will be converted to a negative integer (``sign extension''). On others, a char is promoted

to an int by adding zeros at the left end, and thus is always positive.

下面是 read-null.c 在 x86_64 平台的汇编,x86_64 平台上 gcc char 转 int 会做 sign extension,而且,是和 char 类型比较,直接使用的是 %al,就算没有做符号扩展,%al是返回值的低字节,fgetc()返回EOF 就必定是 0xff

x86_64 机器上 signed char 转 signed int 符号位会扩展,简单说 -1 还是 -1,而且和 char做比较指令上可能就只用了低字节。

没有想到,这快死去的 C 知识居然有用上的一天。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询