Exiting a program with a relevant return value
In this recipe, we'll learn how to exit a C program with a relevant return value. We will look at two different ways to exit a program with a return value and how return
fits together with the system from a broader perspective. We will also learn what some common return values mean.
Getting ready
For this recipe, we only need the GCC compiler and the Make tool.
How to do it…
We will write two different versions of a program here to show you two different methods of exiting. Let's get started:
- We'll start by writing the first version using
return
, which we have seen previously. But this time, we will use it to return from functions, all the way back tomain()
and eventually the parent process, which is the shell. Save the following program in a file calledfunctions_ver1.c
. All the return statements are highlighted in the following code:#include <stdio.h> int func1(void); int func2(void); int main(int argc, char *argv[]) {    printf("Inside main\n");    printf("Calling function one\n");    if (func1())    {       printf("Everything ok from function one\n");       printf("Return with 0 from main - all ok\n");       return 0;    }    else    {       printf("Caught an error from function one\n");       printf("Return with 1 from main - error\n");       return 1;    }    return 0; /* We shouldn't reach this, but                 just in case */ } int func1(void) {    printf("Inside function one\n");    printf("Calling function two\n");    if (func2())    {       printf("Everything ok from function two\n");       return 1;    }    else    {       printf("Caught an error from function two\n");       return 0;    } } int func2(void) {    printf("Inside function two\n");    printf("Returning with 0 (error) from "       "function two\n");    return 0; }
- Now, compile it:
$> gcc functions_ver1.c -o functions_ver1
- Then, run it. Try to follow along and see which functions call and return to which other functions:
$> ./functions-ver1 Inside main Calling function one Inside function one Calling function two Inside function two Returning with 0 (error) from function two Caught an error from function two Caught an error from function one Return with 1 from main – error
- Check the return value:
$> echo $? 1
- Now, we rewrite the preceding program to use
exit()
inside the functions instead. What will happen then is that as soon asexit()
is called, the program will exit with the specified value. Ifexit()
is called inside another function, that function will not return tomain()
first. Save the following program in a new file asfunctions_ver2.c
. All thereturn
andexit
statements are highlighted in the following code:#include <stdio.h> #include <stdlib.h> int func1(void); int func2(void); int main(int argc, char *argv[]) {    printf("Inside main\n");    printf("Calling function one\n");    if (func1())    {       printf("Everything ok from function one\n");       printf("Return with 0 from main - all ok\n");       return 0;    }    else    {       printf("Caught an error from funtcion one\n");       printf("Return with 1 from main - error\n");       return 1;    }    return 0; /* We shouldn't reach this, but just                 in case */ } int func1(void) {    printf("Inside function one\n");    printf("Calling function two\n");    if (func2())    {       printf("Everything ok from function two\n");       exit(0);    }    else    {       printf("Caught an error from function two\n");       exit(1);    } }
- Now, compile this version:
$> gcc functions_ver2.c -o functions_ver2
- Then, run it and see what happens (and compare the output from the previous program):
$> ./functions_ver2 Inside main Calling function one Inside function one Calling function two Inside function two Returning with (error) from function two
- Finally, check the return value:
$> echo $? 1
How it works…
Notice that in C, 0 is regarded as false or error, while anything else is considered to be true (or correct). This is the opposite of the return values to the shell. This can be a bit confusing at first. However, as far as the shell is concerned, 0 is "all ok," while anything else indicates an error.
The difference between the two versions is how the functions and the entire program returns. In the first version, each function returns to the calling function—in the order they were called. In the second version, each function exits with the exit()
function. This means that the program will exit directly and return the specified value to the shell. The second version isn't good practice; it's much better to return to the calling function. If someone else were to use your function in another program, and it suddenly exits the entire program, that would be a big surprise. That's not usually how we do it. However, I wanted to demonstrate the difference between exit()
and return
here.
I also wanted to demonstrate another point. Just as a function returns to its calling function with return
, a program returns to its parent process (usually the shell) in the same way. So, in a way, programs in Linux are treated as functions in a program.
The following diagram shows how Bash calls the program (the upper arrow), which then starts in main()
, which then calls the next function (the arrows to the right), and so on. The arrows returning on the left show how each function returns to the calling function, and then finally to Bash:
There's more…
There are a lot more return codes we can use. The most common ones are the ones we've seen here; 0
for ok and 1
for error. However, all other codes except 0
mean some form of error. Code 1
is a general error, while the other error codes are more specific. There isn't exactly a standard, but there are some commonly used codes. Some of the most common codes are as follows:
Except for these codes, there are some additional ones listed at the end of /usr/include/sysexit.h
. The codes listed in that file range from 64
to 78
and address errors such as data format error, service unavailable, I/O errors, and more.