开发者

Why to use 'errno' at all?

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?


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 a FILE. Every possible return value is also a valid return value, except NULL. So fopen() returns NULL on failure. But then you can't tell what exactly made the function fail. Hence, fopen() uses errno 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 new int, 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:

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

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 than errno or even C, and nobody has ever complained about that.


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

mytime_t t = get_current_time();

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

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

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

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

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 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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜