8

我是 Technion 的一名 CS 学生,我刚刚了解了errno变量和 c 风格的函数调用。这让我想知道,如果 c 风格的系统调用使用寄存器来返回一个值,那么为什么还要使用errno呢?

4

4 回答 4

13

使用的主要原因errno是提供有关错误情况的更多信息。

这在函数的大多数(甚至所有)可能的返回值实际上是有效的返回值的情况下特别有用。

考虑这个fopen()函数,它返回一个指向 a 的指针FILE。每个可能的返回值也是一个有效的返回值,除了NULL. 所以fopen()返回NULL失败。但是,您无法说出究竟是什么导致该功能失败。因此,fopen()用于errno表示确切的错误情况,即文件不存在,或者您没有读取它的权限,或者系统内存不足,或其他任何情况。

您可以将其errno视为一个全局变量(在线程流行之前它曾经是)。如今,errno通常是一个宏包装返回错误条件的函数调用。但这只是 C 实现特定于线程的全局变量的方式。

替代方案errno不太舒服:

您可以为函数提供一个指向 的指针int,并且该函数可以将其错误条件存储在那里。strtod()是这种技术的一个很好的例子。但这使 API 更加复杂,因此不太理想。此外,它会强制程序员定义一个 new int,如果您不关心函数是否失败,这很烦人。

在允许多个返回值(并且没有异常)的语言中,通常返回两个值:一个用于实际结果,另一个用于表示错误条件。在 Go 等语言中,您会看到如下代码:

result, ok = foo();
if (ok) {
    // handle error denoted by "ok"
}

不要相信那些声称这errno是一种“旧”技术的人,因此应该避免。你正在编程的机器比errnoC 语言甚至更古老,而且从来没有人抱怨过这一点。

于 2011-05-15T13:37:27.603 回答
8

C 库的设计是很久以前与早期的 Unix 同时完成的。使用单独的错误代码并不少见(Win32 有类似的 GetLastError())。它有表面上的优势。

如果您决定希望一类函数具有常用的返回值,那么您不能轻易地使用它来返回错误。例如,想象一个假设的 API

mytime_t t = get_current_time();

此 API 的常见用途是获取时间。但在某些情况下它可能会失败,并且您会从 errno 获得详细的错误信息。这使得 API 代码比你不得不说的更容易阅读和编写

mytime_t t=0;
errno_t e = get_current_time(&t);

所以从表面上看,errno 类型的系统很有吸引力。然而,错误状态与实际函数调用的分离会导致很多问题。现代环境使 errno_t 成为每个线程的变量(消除最明显的问题来源),但如果你这样做,你仍然会面临问题

mytime_t t = get_current_time();
mysize_t s = get_window_size();

然后你从第一个函数中无形地破坏 errno。当代码可以在错误路径中运行或根据其他功能实现一个功能时,这会变得更加复杂。确保大量保存和恢复 errno 值。我认为现在这样的系统是脆弱且不可取的,这一点已经被很好地接受了。

许多人使用在显式参数/返回集之外携带错误的异常(许多 C++ 程序员做出这种选择)。在无异常语言或环境中工作的人们倾向于咬紧牙关,为错误保留返回值,并始终通过参数提供输出(COM 使用 HRESULT 做出此选择)。

于 2011-05-15T09:05:08.663 回答
4

errno是一件复杂的事情,因为它是一个历史悠久的界面,现在可能没有人会这样设计。此外,在大多数系统上,它现在看起来只是一个变量,它不是一个。通常它被实现为隐藏函数调用的宏,并且该函数调用返回特定于线程的错误条件。

于 2011-05-15T09:04:12.603 回答
2

我能想到的最好的例子是 stdio 函数fopen,它在失败时返回 NULL,而找出失败原因的唯一方法是通过 errno 和/或 perror。

应该还有其他例子。这正是此刻浮现在脑海中的东西。

于 2011-05-15T08:40:57.737 回答