0

When I read C or C++ code, I often notice that functions return integer values such as -1 or 0. My question is: why are these integer return values used?

It appears that -1 is returned by functions when they are unable to do what they were intended to do. So are these values like HTTP response status codes? If so, how many other values exist and what do they represent?

Ali Haider
  • 378
  • 2
  • 13
  • 5
    This is common in C where there is no built-in error handling mechanism, and sometimes in C++ when exceptions are not appropriate. There is no universal set of error codes, each API defines their own error codes. – François Andrieux May 10 '22 at 21:55
  • 2
    Many (but by no means all) of those routines also return more information through [`std::errno`](https://en.cppreference.com/w/cpp/error/errno). – Eljay May 10 '22 at 22:00
  • Thank you @FrançoisAndrieux for the answer. I'm happy to accept your comment as the answer to the question I posted but I don't see the green tick; so I'm upvoting your comment. – Ali Haider May 10 '22 at 22:03
  • 3
    Case in point: the standard library consistently returns zero (0) on success, some error value on failure. OpenSSL nearly always returns one (1) on success, zero on failure. This is literally to the whim of the architect. The best you can ask is that they remain consistent within their respective implementations. – WhozCraig May 10 '22 at 22:03
  • 1
    A lot of the simplicity of the error messaging harks back to the old days when every bit was sacred. Every bit was great. If a bit was wasted, God got quite irate. – user4581301 May 10 '22 at 22:18
  • 1
    If positive returned values represent something meaningful, but there is also a need to return an error status, one option is to return negative or zero values on error. In the C standard library (and where that is part of C++) a number of functions do exactly that. It avoids needing to return extra values (e.g. by passing extra arguments to receive data or error status) or having to pass/return structures where a single value will do. – Peter May 11 '22 at 02:23

3 Answers3

1

This practice comes from C. Since C does not support exceptions, programmers often use return values as a status that indicates if the function succeeded or not. Some programs use error or status arguments instead, and set them to an error or status code appropriately.

In C++, when throwing an exception isn't appropriate, status codes are used as a way to handle runtime errors. If your function could fail in situations where it's not convenient (for example, within a destructor), you can return a status code from the function instead of throwing, so the error can be dismissed safely.

There isn't a standard or guidelines about how to implement status codes and generally it depends in the programmer. If you're interested in knowing what these status codes mean you should check the reference for the application/library you're using.

Coal
  • 346
  • 3
  • 9
  • 4
    Could deal with a better example. You have to do all manner of stupid to return a result code from a destructor. – user4581301 May 10 '22 at 22:41
  • @user4581301 - Functions ran inside the destructor can still throw. One could wrap these calls in a `try/catch` block or use status codes to handle errors. – Coal May 11 '22 at 15:33
  • However, in case of using `try/catch`, the exception will be re-thrown implicitly once the function-try-block handler of a destructor (or constructor) finishes execution, unless `return` is used explicitly to prevent this. [See here](https://en.cppreference.com/w/cpp/language/function-try-block) – Coal May 11 '22 at 15:40
  • Understood. My point is you're talking about using return codes where exceptions are not appropriate, then picked an example where you cannot return a value either. – user4581301 May 11 '22 at 16:10
  • @user4581301 - Seems to be fault of my ambiguous phrasing. I clarified what I meant in the edit. Thanks for pointing it out. – Coal May 11 '22 at 22:41
1

I assume that you refer to the return value of main. This is what the C++ standard says about it:

[basic.start.main]

A return statement ([stmt.return]) in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std​::​exit with the return value as the argument.


[support.start.term]

[[noreturn]] void exit(int status);

Effects:

  • ...
  • Finally, control is returned to the host environment. If status is zero or EXIT_­SUCCESS, an implementation-defined form of the status successful termination is returned. If status is EXIT_­FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.

So, the meaning of the return value is largely implementation-defined.

Many operating system (such as Linux, Windows, Mac, etc.) shells have a concept of "exit status code". Typically, implementations of C++ forward the returned value as the exit status code. The meaning of such status may depend on the environment where the program runs.

For example, this is what the manual of bash (which is a shell for Linux) says:

... Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially. ...

For the shell’s purposes, a command which exits with a zero exit status has succeeded. A non-zero exit status indicates failure.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • @Coal Sure it can. `main` returns a value of type `int` and -1 is representable as `int` and thus you can return -1 from `main`. In case of bash that I used as an example, the environment doesn't specify what it means to return -1. – eerorika May 11 '22 at 16:08
1

A tradition from early days of digital computing, the reason being that setting accumulator to zero would automatically set the processor zero flag - which could be simply checked by a conditional branch instruction. Other values would reqire an extra arithmetic instruction before branching. So if all was needed was to check for existence of an error, a zero check would be very cheap. Other values could be documented as error identifying numbers with the special value -1 as the generic code for undocumented errors. On a 2's complement machine - the dominant architecture - a value of -1 sets all bits to 1: the logic inverse of a zero. Specific sequences of instructions might lead to setting a specific processor flag too, which would simplify checking for -1 like zero. Nowadays there are more complicated error handling mechanisms. The most famous being try-throw-catch exception handling. But we are generally recommended against using exceptions that way. Because it complicates design and creates development cycle problems. Better solutions can benefit from std::error_condition, std::optional or std::expected. The later being a subjugation of former two shipping with C++23.

Red.Wave
  • 2,790
  • 11
  • 17