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
是文本文件,empty
是长度为零的文件):
% ./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
?
答案 0 :(得分:4)
errno
不是特定操作的结果,而是构建高级FILE *stdin
stdio结构时支持操作的结果。
Libc通常会调用stat()
,其他几个和lseek()
系统调用失败,因为/dev/
中的特定文件不是常规文件。它可能取决于特定的Libc实现,因此您在各种系统上看到的差异。
这并不代表您的操作失败,只有在返回错误代码的情况下才应检查错误。
在Linux上,您可以使用命令strace
来获取程序正在调用的所有系统调用的列表以及导致错误的所有系统调用的列表:
strace ./my program
其他系统也有类似的工具。
答案 1 :(得分:2)
我不会说它是预期的行为(事实上,我无法在我方便的系统上重现它),但是C标准和Posix都允许这样做。 / p>
C标准和Posix通常禁止库函数将errno
设置为0,因此无法清除errno
表示成功。但是,C允许errno
由库函数修改“是否存在错误,前提是errno
的使用没有记录在
函数描述“(§7.5第3段).C标准没有记录errno
对fgetc
的使用。
Posix更灵活:它指定了
除非该函数的描述指定不应修改errno,否则未指定成功调用函数后的errno设置。
它确实记录了errno
fgetc
的使用情况,但该文档并未表明成功调用不会修改errno
,只是应该通过读错误。
具体来说,Posix所说的是
如果发生读取错误,则应设置流的错误指示符,
fgetc()
应返回EOF
,并设置errno
以指示错误。
换句话说,EOF
可能表示成功调用(因为文件末尾不是错误)或者它可能表示错误。仅在后一种情况下errno
设置为有意义的值。因此,在检查errno
之前,您需要验证ferror(stdin)
是否报告错误。
因此,正如我所说,标准允许这种行为。为什么fgetc
实现会这样做?我的猜测是你的系统上的libc
实现在第一次调用时正在进行某种延迟初始化。