C中的简单测试程序名为get1:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
errno = 0;
int ch = fgetc(stdin);
printf("ch = %d\n", ch);
if (errno)
printf("errno = %d: %s\n", errno, strerror(errno));
return 0;
}
它只打印以十进制读取的第一个字节,然后显示errno和相关的错误消息,如果errno不为零.
一些结果(foo是一个文本文件,空是一个长度为零的文件):
% ./get1 < foo
ch = 104
% ./get1 < empty
ch = -1
好的,正如所料.然而:
% ./get1 < /dev/zero
ch = 0
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/null
ch = -1
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/random
ch = 196
errno = 25: Inappropriate ioctl for device
读取工作正常,但是当我从这些设备中读取时,它正在设置errno.为什么?
那是在macOS(达尔文)上.我在Linux(Debian)和NetBSD上得到了同样的东西,除了针对/ dev / random的不同错误(其他设备上的错误与macOS上的错误相同):
% ./get1 < /dev/random
ch = 170
errno = 22: Invalid argument
据我所知,如果你没有得到EOF,那就没有错误.如果你得到EOF,那么你检查errno以查看是否有错误.在所有这些情况下,从设备获取字节似乎没有错误,也不应该存在.然而,errno已定.
(如果有人想知道,删除printf不会改变任何东西.)
事实证明,在AIX,SunOS或OpenSUSE上从这样的设备读取时,不会设置errno.
那么这里的交易是什么?这是一个错误,尽管很普遍吗?这是可接受的行为吗?这是预期的行为吗?为什么设置errno?
解决方法:
我不会说它是预期的行为(实际上,我不能在我方便的系统上重现它),但是C标准和Posix都允许这样做.
C标准和Posix通常都禁止库函数将errno设置为0,因此它们无法清除errno以指示成功.但是,C允许通过库函数修改errno“是否存在错误,前提是errno的使用没有记录在
函数描述“(§7.5第3段).C标准没有记录fgetc对errno的使用.
Posix更灵活:它指定了
The setting of errno after a successful call to a function is unspecified unless the description of that function specifies that errno shall not be modified.
它确实记录了fgetc对errno的使用,但该文档并未指出errno不会被成功的调用修改,只是它应该被读错误修改.
具体而言,Posix所说的是
If a read error occurs, the error indicator for the stream shall be set,
fgetc()
shall returnEOF
, and shall seterrno
to indicate the error.
换句话说,EOF可能表示调用成功(因为文件末尾不是错误)或者它可能表示错误.只有在后一种情况下才将errno设置为有意义的值.因此,在检查errno之前,您需要验证ferror(stdin)是否报告错误.
因此,正如我所说,标准允许这种行为.为什么fgetc实现会这样做?我的猜测是你的系统上的libc实现在第一次调用时正在进行某种延迟初始化.