tags:

views:

1521

answers:

12

Hi, Is there a reason why zero is used as a "default" function return value? I noticed that several functions from the stdlib and almost everywhere else, when not returning a proper number (e.g pow(), strcpy()) or an error (negative numbers), simply return zero.

I just became curious after seeing several tests performed with negated logic. Very confusing.

Why not return 1, or 0xff, or any positive number for that matter?

+2  A: 

Because Bash and most other UNIX shell environments regard 0 as success, and -x as a system error, and x as a user-defined error.

Paul Betts
Since when is -x a system error code? Aren't all error codes 0..255 on Unix systems?
strager
255 could just as well be an unsigned -127 :)
majelbstoat
@majelbstoat, don't you mean -128? =] Regardless; non-zero is error, zero is success, on Unix shells. It doesn't matter what the value really is if it's an error, unless it has a special meaning (which is rare).
strager
Ah, I was thinking of kernel mode, where you always see lines like:return -ENOENT;
Paul Betts
@strager, indeed I do, can I call typo? :) Ok, momentary brain freeze then. Standard Unix error codes are 1 - 127 I believe (this should have been my clue), so perhaps the additional numbers are what Paul Betts is referring to. (Not that applications have to use them, of course.)
majelbstoat
A number greater than 127 (or negative when interpreted as signed) normally means that that the process was terminated with a signal.
Arkadiy
+4  A: 

In shell scripting, 0 represents true, where another number typically represents an error code. Returning 0 from a main application means everything went successfully. The same logic may be being applied to the library code.

It could also just be that they return nothing, which is interpreted as 0. (Essentially the same concept.)

majelbstoat
It doesn't really have anything to do with shell scripting. It has to do with the C/C++ language.
carson
C pretty much evolved on Unix, and Unix pretty much evolved with C. I think that's why @majelbstoat brought this up.
strager
I think he was talking about functions other than main(). Main returns 0 for this reason, but other functions don't have the same constraints.
Drew Hall
@Drew Hall, Sure, see the last sentence of the first paragraph :) But, point taken.@strager, Yes, that's why I mentioned it.
majelbstoat
@Majelbstoat: Whoops--sorry I didn't read your answer closely enough.
Drew Hall
A: 

Because 0 is false and null in C/C++ and you can make handy short cuts when that happens.

carson
+1  A: 

There's probably a bunch of forgotten history dating back to the days when everything was written in asm. In general it is much easier to test for zero than for other specific values.

HUAGHAGUAH
8086: "or ax, ax" checks for zero (sets ZERO flag if ax=0). "xor ax, ax" clears to zero (sets ax=0). Each opcode is one byte, and runs quicker than their cmp and mov counterparts.
strager
"test eax, eax" is another commonly used (fast) instruction to test for zero
Adam Rosenfield
+16  A: 

Originally, C did not have "void". If a function didn't return anything, you just left the return type in the declaration blank. But that meant, that it return an int.

So, everything return something, even if it didn't mean anything. And, if you didn't specifically provide a return value, whatever value happened to be in the register the compiler used to return values because the function's return value.

// Perfectly good K&R C code.
NoReturn()
{
   // do stuff;
   return;
}

int unknownValue = NoReturn();

People took to clearing that to zero to avoid problems.

James Curran
+1  A: 

I may be wrong about this, but I think that it's mainly for historical reasons (hysterical raisins?). I believe that K&R C (pre-ANSI) didn't have a void type, so the logical way to write a function that didn't return anything interesting was to have it return 0.

Surely somebody here will correct me if I'm wrong... :)

Drew Hall
+17  A: 

The rationale is that you want to distinguish the set of all the possible (negative) return values corresponding to different errors from the only situation in which all went OK. The simplest, most concise and most C-ish way to pursue such distinction is a logical test, and since in C all integers are "true" except for zero, you want to return zero to mean "the only situation", i.e. you want zero as the "good" value.

The same line of reasoning applies to the return values of Unix programs, but indeed in the tests within Unix shell scripts the logic is inverted: a return value of 0 means "true" (for example, look at the return value of /bin/true).

Federico Ramponi
A: 

It is because when used from a UNIX shell a command that returns 0 indicates success.

Any other value indicates a failure. As Paul Betts indicates positive and negative values delimitate where the error probably originated, but this is only a convention and not an absolute. A user application may return a negative value without any bad consequence (other than it is indicating to the shell that the application failed).

Martin York
+1  A: 

My understanding is that it was related to the behaviour of system calls.

Consider the open() system call; if it is successful, it returns a non-negative integer, which is the file descriptor that was created. However, down at the assembler level (where there's a special, non-C instruction that traps into the kernel), when an error is returned, it is returned as a negative value. When it detects an error return, the C code wrapper around the system call stores the negated value into errno (so errno has a positive value), and the function returns -1.

For some other system calls, the negative return code at the assembler level is still negated and placed into errno and -1 is returned. However, these system calls have no special value to return, so zero was chosen to indicate success. Clearly, there is a large variety of system calls, but most manage to fit these conventions. For example, stat() and its relatives return a structure, but a pointer to that structure is passed as an input parameter, and the return value is a 0 or -1 status. Even signal() manages it; -1 was SIG_DFL and 0 was SIG_IGN, and other values were function pointers. There are a few system calls with no error return - getpid(), getuid() and so on.

This zero-indicates-success mechanism was then emulated by other functions which were not actually system calls.

Jonathan Leffler
+3  A: 

Another (minor) reason has to do with machine-level speed and code size.

In most processors, any operation that results in a zero automatically sets the zero flag, and there is a very cheap operation to jump against the zero flag.

In other words, if the last machine operation (e.g., PUSH) got us to zero, all we need is a jump-on-zero or a jump-not-zero.

On the other hand, if we test things against some other value, then we have to move that value into the register, run a compare operation that essentially subtracts the two numbers, and equality results in our zero.

Uri
A: 

Conventionally, a return code of 0 specifies that your program has ended normally and all is well. (You can remember this as "zero errors", although for technical reasons, you cannot use the number of errors your program found as the return code. See Style.) A return code other than 0 indicates that some sort of error has occurred. If your code terminates when it encounters an error, use exit, and specify a non-zero return code. Source

mahesh
A: 

Besides all the fine points made by previous posters, it also cleans up the code considerably when a function returns 0 on success.

Consider:

if ( somefunc() ) {
   // handle error
}

is much cleaner than:

if ( !somefunc() ) {
   // handle error
}

or:

if ( somefunc() == somevalue ) {
   // handle error 
}
joveha