15

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?

Marcelo MD
  • 1,769
  • 1
  • 18
  • 23
  • somehow i think this should go in the wiki, i love all these different reasons people find (and i think many of them are valid) – kritzikratzi Sep 13 '12 at 02:52
  • Yeah, probably there is no single right answer. I think they're all plausible and certainly valid on their own. I certainly don't have the knowledge to be the judge. – Marcelo MD Sep 20 '12 at 18:40

12 Answers12

24

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 A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
21

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 returned an int.

So, everything returned 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 became 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
  • 101,701
  • 37
  • 181
  • 258
4

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
  • 12,889
  • 4
  • 28
  • 26
  • It doesn't really have anything to do with shell scripting. It has to do with the C/C++ language. – carson Dec 01 '08 at 03:34
  • 1
    C pretty much evolved on Unix, and Unix pretty much evolved with C. I think that's why @majelbstoat brought this up. – strager Dec 01 '08 at 03:39
  • 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 Dec 01 '08 at 03:41
  • @Drew Hall, Sure, see the last sentence of the first paragraph :) But, point taken. @strager, Yes, that's why I mentioned it. – majelbstoat Dec 01 '08 at 03:45
  • @Majelbstoat: Whoops--sorry I didn't read your answer closely enough. – Drew Hall Dec 01 '08 at 03:49
4

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
  • 88,451
  • 51
  • 221
  • 321
  • 1
    On the CPUs I did assembler in (Z-80/Z-8000/8088) only an arithmetic operation resulting in zero set the flag. A move or push would not. – James Curran Jan 08 '11 at 07:39
2

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.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • Since when is -x a system error code? Aren't all error codes 0..255 on Unix systems? – strager Dec 01 '08 at 03:40
  • 255 could just as well be an unsigned -127 :) – majelbstoat Dec 01 '08 at 03:47
  • 1
    @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 Dec 01 '08 at 03:54
  • 1
    Ah, I was thinking of kernel mode, where you always see lines like: return -ENOENT; – Ana Betts Dec 01 '08 at 04:01
  • @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 Dec 01 '08 at 04:02
  • A number greater than 127 (or negative when interpreted as signed) normally means that that the process was terminated with a signal. –  Dec 01 '08 at 14:16
1

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
  • 1,063
  • 6
  • 3
  • 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 Dec 01 '08 at 03:42
  • 1
    "test eax, eax" is another commonly used (fast) instruction to test for zero – Adam Rosenfield Dec 01 '08 at 03:46
  • The zero flag is quite a bit older than the x86; that's from IBM's System/360 (1964). It was also used on the PDP-11 from C's earliest days. – MSalters Jun 29 '21 at 14:01
1

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
  • 28,429
  • 12
  • 61
  • 81
1

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
  • 730,956
  • 141
  • 904
  • 1,278
1

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

Priyanka Mishra
  • 10,400
  • 18
  • 62
  • 75
0

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

carson
  • 5,751
  • 3
  • 24
  • 25
0

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
  • 257,169
  • 86
  • 333
  • 562
0

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
  • 2,599
  • 2
  • 17
  • 19
  • 1
    I've never been a fan of this, because it doesn't read well, despite being more clean. `if(doMyOperation())` sounds like "if the operation was done", not "if the operation failed" – yano Apr 10 '16 at 00:11