C 当POSIX函数返回-1或NULL指示错误条件时,为什么会出现errno

C 当POSIX函数返回-1或NULL指示错误条件时,为什么会出现errno,c,posix,errno,C,Posix,Errno,当其中一个UNIX系统函数发生错误时,通常会返回负值,并将整数errno设置为一个值,该值提供了附加信息。 --UNIX环境中的高级编程,第1.7节 这似乎很奇怪: 它引入了标准库的不同编译单元之间的耦合——导致错误的模块中没有定义错误状态 它引入了实现复杂性,因为errno需要是线程本地的 它引入了可用性复杂性,因为用户需要检查原始系统调用是否有错误,然后检查errno,这也是另一个函数调用 为什么不在返回值中对错误状态进行编码?主要是出于历史原因 请注意,在现实中,errno现在不是一个

当其中一个UNIX系统函数发生错误时,通常会返回负值,并将整数
errno
设置为一个值,该值提供了附加信息。 --UNIX环境中的高级编程,第1.7节

这似乎很奇怪:

  • 它引入了标准库的不同编译单元之间的耦合——导致错误的模块中没有定义错误状态
  • 它引入了实现复杂性,因为
    errno
    需要是线程本地的
  • 它引入了可用性复杂性,因为用户需要检查原始系统调用是否有错误,然后检查
    errno
    ,这也是另一个函数调用

为什么不在返回值中对错误状态进行编码?

主要是出于历史原因

请注意,在现实中,
errno
现在不是一个普通的全局变量(20世纪80年代就是这种情况)。现在(C99,C11…)是一个宏-通常扩展为一些函数调用,可能是
\uu errno()
(最近的C标准要求
errno
是一个宏,请参见§7.5);或者它可能被扩展到一些线程局部变量,甚至一些编译器魔术

errno
希望成为满足多线程需求的宏,所以我猜标准的发展要求它是某种宏

因此,您应该
#包括
并使用
errno
宏,就像它是某个全局变量一样,但要知道事实上它不是一个全局变量

细节是具体实施的。查看C标准库的源代码,例如

在公共标题中:

 int *__errno_location(void);
 #define errno (*__errno_location())
GNULIBC也有相似之处

顺便说一句,有些系统函数不返回整数(例如
mmap
),有些POSIX函数不通过
errno
指示错误,例如
dlopen
(请参见
dlerror
)。因此,在某些标准中,很难保证每个错误都可以由返回值表示。

我将对此进行详细说明

“errno”是UNIX中为数不多的几个非常糟糕的愚蠢行为之一。Linux已经 已修复,不在内部使用,永远不会。太糟糕了 用户空间必须修复正确的错误代码,返回 内核会这样做,并将其转化为向后的“errno”愚蠢 兼容性

[……]

“厄尔诺”是那些根本不应该发生的事情之一 存在。它在最初的UNIX中是错误的,现在是错误的

[……]

Linux返回负错误数的方法要好得多。它是 本质上是线程安全的,并且它没有性能方面的缺点。当然,它 这取决于是否有足够的结果域 将错误返回与良好返回分开,但对于 所有系统调用


假设这种机制是在线程之前发明的,这是非常安全的。另外,为了有点迂腐,检查
errno
不一定是函数调用。而且,函数返回值在许多情况下用于返回实际数据。请参阅。
read()
是一个不好的示例,特别是在这里返回
-errno
是可行的,假设它返回的是一个
有符号的
整数,而它不返回@AndrewHenle@alk当然返回
ssize\u t
,即
size\u t
的签名表亲?@alk-OK,但错误代码通常不能与有效返回值分离的观点仍然成立。要求
errno
必须是宏,禁止或隐藏宏是未定义的行为。@AndrewHenle:那又怎样?@alk
errno
始终是宏。目前,它的资格是“非常经常”。考虑到答案的作者是谁,我强烈怀疑他的目的是完全正确。啊,我明白了。很公平@AndrewHenle@AndrewHenle:我改进了我的答案,并把“作者是谁”作为一种恭维。。。
 int *__errno_location(void);
 #define errno (*__errno_location())