为什么要使用“errno”?根本吗?
我是以色列理工学院的一名计算机科学学生,我刚刚学习了 errno
变量和 C 风格的函数调用。 这让我想知道,如果 c 风格的系统调用使用寄存器来返回值,为什么有人应该使用 errno 呢?
I'm a CS student at the Technion, I have just learned of errno
variable and c-style function calls.
This makes me wonder, if c-style syscalls use registers to return a value, why should anyone use errno
at all?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用
errno
的主要原因是提供有关错误情况的更多信息。这在函数的大多数(甚至所有)可能的返回值实际上是有效返回值的情况下特别有用。
考虑一下
fopen()
函数,它返回一个指向FILE
的指针。除了NULL
之外,每个可能的返回值也是有效的返回值。因此,fopen()
在失败时返回NULL
。但这样你就无法判断到底是什么导致了该函数失败。因此,fopen()
使用errno
来表示确切的错误情况,即文件不存在,或者您没有权限读取它,或者系统内存不足,或者其他什么。您可以将 errno 视为全局变量(在线程流行之前一直如此)。如今,
errno
通常是一个包装返回错误条件的函数调用的宏。但这只是 C 实现线程特定全局变量的方式。errno
的替代方案不太方便:您可以提供一个带有指向
int
的指针的函数,并且该函数可以在那里存储其错误条件。strtod()
是这种技术的一个很好的例子。但这使得 API 更加复杂,因此不太理想。此外,它还强制程序员定义一个新的 int,如果您不关心函数是否失败,这会很烦人。在允许多个返回值(并且不具有异常)的语言中,通常返回两个值:一个用于实际结果,另一个表示错误条件。在像 Go 这样的语言中,您会看到如下代码:
不要相信那些声称 errno 是一种“旧”技术并因此应该避免的人。您正在编程的机器比 errno 甚至 C 都古老得多,而且没有人抱怨过这一点。
The main reason for using
errno
is to give more information about the error condition.This is especially useful in situations where most (or even all) possible return values of a function are actually valid return values.
Consider the
fopen()
function, which returns a pointer to aFILE
. Every possible return value is also a valid return value, exceptNULL
. Sofopen()
returnsNULL
on failure. But then you can't tell what exactly made the function fail. Hence,fopen()
useserrno
to denote the exact error condition, i.e. the file doesn't exist, or you don't have permission to read it, or the system is out of memory, or whatever.You can think of
errno
as a global variable (which it used to be until threads became popular). Nowadays,errno
is usually a macro wrapping a function call returning the error condition. But this is just C's way of implementing thread-specific global variables.The alternatives to
errno
are less comfortable:You could supply a function with a pointer to an
int
, and the function can store its error condition there.strtod()
is a good example of this technique. But this makes the API more complicated and hence less desirable. Also, it forces the programmer to define a newint
, which is annoying if you don't care if the function fails.In languages that allow more than one return value (and don't feature exceptions), it's common to return two values: one for the actual result and another to denote the error condition. In languages like Go, you see code like the following:
Don't trust people who claim that
errno
is an "old" technique and hence to be avoided. The machine you're programming is far older thanerrno
or even C, and nobody has ever complained about that.C库的设计是很久以前与早期Unix同时完成的。使用单独的错误代码并不是一种罕见的模式(Win32 有类似的 GetLastError())。它有表面上的优势。
如果您决定希望一类函数具有常用的返回值,那么您就不能轻松地使用它来返回错误。例如,想象一个假设的 API。
该 API 的常见用途是获取时间。但在某些情况下它可能会失败,并且您可以从 errno 获取详细的错误信息。这使得 API 代码比您不得不说的更容易阅读和编写。
所以从表面上看,errno 类型的系统很有吸引力。然而,将错误状态与实际函数调用分离会导致很多问题。现代环境使 errno_t 成为每线程变量(消除了最明显的问题根源),但您仍然面临这样的问题:如果
这样做,则会无形中销毁第一个函数中的 errno。当代码可以在错误路径中运行或者一个函数是根据其他函数来实现时,情况会变得更加复杂。确保大量保存和恢复 errno 值。我认为现在人们已经普遍接受这样的系统是脆弱且不可取的。
许多人使用异常,这些异常携带显式参数/返回集之外的错误(许多 C++ 程序员都做出这种选择)。在无异常语言或环境中工作的人们往往会硬着头皮保留错误的返回值,并始终通过参数提供输出(COM 通过 HRESULT 进行此选择)。
The design of the C library was done a long time ago at the same time as early Unix. Use of a separate error code is not an uncommon pattern (Win32 has the similar GetLastError()). And it has superficial advantages.
If you decide you want a class of functions to have a return value that is commonly used, then you can't easily use that to return errors. For example, imagine a hypothetical API
The common use of this API is to get time. But perhaps it can fail in some circumstances, and you get detailed error information from errno. This makes the API code a bit easier to read and write than if you had to say
So superficially, errno-type systems are appealing. However, the separation of error state from the actual function call leads to lots of problems. Modern environments make errno_t a per-thread variable (removing the most obvious source of problems), but you still face the problem that if you do
then you destroy the errno from the first function invisibly. This gets even more complex when code can run in error paths or where one function is implemented in terms of others. Lots of saving and restoring errno values ensures. I think it's pretty well accepted now that such systems are fragile and undesirable.
Many folks use exceptions which carry errors outside the explicit parameter/return set (many C++ programmers make this choice). Folks working in exception-free languages or environments tend to bite the bullet and reserve the return value for an error and always provide output through parameters (COM makes this choice with HRESULTs).
errno 是一件复杂的事情,因为它是一个历史悠久的界面,现在可能没有人会这样设计。此外,在大多数系统上,现在它看起来只是一个变量,而不是一个变量。通常它被实现为隐藏函数调用的宏,并且该函数调用返回线程特定的错误条件。
errno
is a complicated thing, in that it is an historic interface that probably nowadays nobody would design like that. In addition on most systems it nowadays only looks like a variable, it ain't one. Usually it is implemented as a macro that hides a function call, and that function call returns a thread specific error condition.我能想到的最好的例子是 stdio 函数 fopen,它在失败时返回 NULL,找出失败原因的唯一方法是通过 errno 和/或 perror。
一定还有其他例子。这只是此刻浮现在脑海中的事情。
The best example that I can think of is the stdio function
fopen
, which returns NULL at failure and the only way to find out why it failed is through errno and/or perror.There must be other examples. This is just what sprung in to mind at the moment.