当编译以下代码时,它进入无限循环:
int main()
{
unsigned char ch;
FILE *fp;
fp = fopen("abc","r");
if(fp==NULL)
{
printf("Unable to Open");
exit(1);
}
while((ch = fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
printf("\n",ch);
return 0;
}
gcc编译器还会在编译时发出警告
abc.c:13:warning: comparison is always true due to limited range of data type
当unsigned char
被char
或int
替换为预期时,代码运行正常,即终止。
但代码也适用于unsigned int
。
正如我在EOF
中读到的那样在-1
中定义为stdio.h
然后为什么这个代码对unsigned char失败,但对unsigned int运行正常。
答案 0 :(得分:8)
写这一行的黄金法则是
while ((ch = fgetc(stdin)) != EOF)
ch
应为int
。让ch
无符号失败的可爱技巧失败,因为EOF
是一个有符号的int数量。
好的,我们现在进入深度......
第1步:
ch=fgetc(fp)
fgetc()
返回-1
(签名int
)。通过C ch
的黄金规则获得所有1
的最后一个八位字节。因此值255
。执行
ch
的字节模式
ch = fgetc(fp);
因此将是
11111111
第2步:
ch != EOF
现在EOF
是有符号整数,ch
是 unsigned char
......
我再次提到C的黄金法则......在比较之前,较小的人 ch
会转换为大尺寸 int
它的字节模式现在是
00000000000000000000000011111111 = (255)10
而EOF
是
11111111111111111111111111111111 = (-1)10
它们无法平等.......因此,引导以下while循环的语句
while ((ch = fgetc(stdin)) != EOF)
永远不会评估为假...
因此无限循环。
答案 1 :(得分:7)
有几个隐式转换正在进行中。它们与特定警告并不真正相关,但我将它们包含在这个答案中,以显示编译器对该表达式的真正作用。
所以表达式等同于
(unsigned char)ch != (int)EOF
C中的整数提升规则将隐式地将unsigned char转换为unsigned int:
(unsigned int)ch != (int)EOF
然后C中的平衡规则(又名通常的算术转换)将隐式地将int转换为unsigned int,因为每个操作数必须具有相同的类型:
(unsigned int)ch != (unsigned int)EOF
在您的编译器上EOF可能是-1:
(unsigned int)ch != (unsigned int)-1
,假设32位CPU,与
相同(unsigned int)ch != 0xFFFFFFFFu
角色永远不会有这么高的值,因此警告。
答案 2 :(得分:2)
你需要使用int
fgetc()返回一个特定的int,以便它可以指示文件的结尾
它使用signed char运行正常,因为EOF(-1)在范围内,但是如果你读入一个值大于127的char,它会破坏。
使用int,在检查EOF
后将其强制转换为char答案 3 :(得分:2)
我也遇到过这个问题。我的解决方案是使用feof()。
unsigned int xxFunc(){
FILE *fin;
unsigned char c;
fin = fopen("...", "rb");
if(feof(fin) != 0) return EOF;
c = fgetc(fin);
fclose(fin);
...
}
您可以定义一个int变量来与EOF进行比较。例如:
int flag = xxFunc();
while(flag != EOF) {...}
这适合我。
** 重要更新 * ** 强>
使用我之前提到的方法后,我发现了一个严重的问题。 feof()不是打破while循环的好方法。 这就是它的原因。 http://www.gidnetwork.com/b-58.html
所以我找到了一个更好的方法来做到这一点。我用一个int变量来做。这里:
int flag;
unsigned char c;
while((flag = fgetc(fin)) != EOF)
{
//so, you are using flag to receive, but transfer the value to c later.
c = flag;
...
}
经过我的测试,这是有效的。
答案 4 :(得分:0)
将unsigned int与signed int进行比较时,会将signed int转换为unsigned int并进行比较。因此,当您使用unsigned int'ch'读取文件时,读取EOF会给出2 ^ 32 + 1(在4字节int机器上),并且在将其与EOF进行比较时,它会将EOF转换为unsigned,这也是2 ^ 32 + 1,因此程序停止!
如果你使用unsigned char ch,当你读取文件时,读取EOF返回2 ^ 32 + 1,这将被转换为unsigned char,它将值截断为前8位(在1字节char机器上)并输出255.因此,你比较255和2 ^ 32 + 1,导致无限循环。
这里的问题是在比较之前截断。
如果您使用
while((ch = fgetc(fp))!=(unsigned char)EOF)
printf("%c",ch);
你的程序运行正常!
答案 5 :(得分:0)
使用这种实现产生lint警告
比较'char'类型与EOF
// read the data in a buffer
611 ch = getc(csv_file);
612 while (ch != EOF)
FIX:
// read the data in a buffer
while ((ch = getc(csv_file)) != EOF)