Importance of return value from main function in C

By | 03/09/2012

Most of the times people who are new to software programming know the importance of a return value from any normal function as it can be easily seen how a returned value is being handled by the caller function but generally these newbies are not aware of the importance of returning appropriate value from main function in C as one does not have the visibility of returned value from main() function. So, In this brief article, we will understand the importance of return value from main function in C by using a practical example.

Return value from main function in C

The example code

Following is the example C code that we will use in this article:

#include<stdio.h>
#include<string.h>

#define SUCCESS 0
#define FAILURE -1

int main(void)
{
   char pass_buff[50] = {0};
   printf("\n Enter the password...");

   //Get the password from user
   fgets(pass_buff,sizeof(pass_buff)-1,stdin);

   //Make sure that the extra(last) character
   //picked up from stdin is washed off 
   //from buffer.
   pass_buff[strlen(pass_buff)-1] = '\0';

   if(! (strcmp(pass_buff,"Linux")))
   {
       // Passwords match
       printf("\n Passwords Match..SUCCESS\n");
       return SUCCESS;
   }
   else
   {
       // Passwords do not macth
       printf("\n Passwords do not match...FAILURE\n");
       return FAILURE;
   }
}

The code above is a very basic logic of a password screening software that matches the password entered by user with the actual password. On the basis of the matching result it either returns 0 or returns 1 from main() function.

Now if we compile and execute it using gcc, a sample run may look like :

$ gcc -Wall return.c -o return
$ ./return 

 Enter the password...Linux

 Passwords Match..SUCCESS

So we see that by providing a correct password, success is returned.

In Linux system programming, a general thumb rule is followed that if a function is successful then it returns 0 otherwise it returns a non-zero value. This rule is just a thumb rule and not a mandatory one as there are some standard functions that do not follow this rule but still, most of the standard functions follow it. In our case too, the main() function returns 0 in case of success and a non-zero value in case of failure.

In Linux, whatever value the main() function returns becomes the return value of the executable. This means that the value returned by the main() function of an executable can be accessed to determine whether the executable passed or failed.

This value can be accessed on shell using the ‘echo $?‘ command which outputs the return value of the last command run on shell. Lets use this command in case of our executable. Following is the output :

$ ./return 

 Enter the password...Linux

 Passwords Match..SUCCESS
$ echo $?
0

$ ./return 

 Enter the password...Unix

 Passwords do not match...FAILURE
$ echo $? 
1

$

So we see that the ‘echo $?’ command returned exactly the same value what main() would have returned in case of those inputs.

The most popular use of return values of executables is when they are used from within the scripts. A script when executes an executable, it mostly needs to determine whether the executable ran successfully or not.

A sample bash script for checking return value of our executable may look like :

#!/bin/bash
./return
if [ $? -eq 0 ] ; then
        echo "SCRIPT :: Program ran successfully"
else
        echo "SCRIPT :: Program failure"
fi

Now, when this script is run :

$ ./script.sh 

 Enter the password...Linux

 Passwords Match..SUCCESS
SCRIPT :: Program ran successfully

So we see that script was able to detect success of executable on the basis of its return value. So its always advisable to keep track of what is being returned from the main() function of an executable.

Note : I use Linux Mint with gcc compiler version 4.4.3

5 thoughts on “Importance of return value from main function in C

  1. not required

    Still it does not say about the importance of return value of main(). Like you said, it is not visible so newbies cannot appreciate this. But all you have written is about how to see the return value in main, not its importance.

    Reply
  2. Martin Drozdík

    Hello,

    Please, could you explain why echo $? prints 1 when main returned -1?

    Reply
    1. Carsten Holtkamp

      The answer is, it doesn’t!
      The exit function is declared as void exit (int status).
      Status is defined as value between 0 and 255.

      The status value is truncated to eight bits.

      #define FAILURE -1

      leads to undefinded behaviour.
      On my machine it returns 255.

      Status is the program’s exit status, which becomes part of the process’ termination status.
      Don’t confuse a program’s exit status with a process’ termination status.

      You could make use of the macros EXIT_SUCCESS and EXIT_FAILURE which are defined in stdlib.h.

      #include
      exit (EXIT_SUCCESS)
      exit (EXIT_FAILURE)

      As an example:
      https://ideone.com/c4j2Ps

      Further reading:
      GLibC:Program-Termination
      http://www.gnu.org/software/libc/manual/html_node/Program-Termination.html

      Coding-Style:
      https://www.kernel.org/doc/Documentation/CodingStyle

      Reply
      1. Martin Drozdík

        Thank you for the detailed explanation.

        So if I understand correctly:

        1. There is a mistake in the article.
        2. We should return either EXIT_SUCCESS or EXIT_FAILURE.
        3. There is no standard correspondence between the process’ termination status (echo $?) and the program’s exit status, returned by main.

        Reply
        1. Carsten Holtkamp

          Hey Martin,
          I needed some time to think about your questions. I am not a pro Kernel Dev
          and I do not have 10 Years + in System or Application Development.
          I do have some knowledge in different fields and my focus lies on x86_64
          Assembler and GNU/Linux C-Coding. I tried my best to give reliable, verbose, but not too complex answers without losing the red line.

          To answer your Questions (hopefully):
          =====================================
          1. Is there a mistake in the article?

          I wouldn’t call it a mistake to define FAILURE as -1 in this case.
          In Error Handling functions often return -1 to indicate an error, described via a special variable ‘errno’ (Error Number). GLibC provides ernno support for library and system calls (see $ man 3 errno).
          But that is Error Handling.

          The example code from the article works under normal circumstances and it is useable in a Shell Script, ’cause the program returns something else than 0 if the password is incorrect.

          if [ “$?” != “0” ]; then

          fi

          That means you could write into a log file with this return values:
          If not 0 (!= “0”), log to file (> foo.log), ‘who'(whoami) did that ‘when'(date %s), as an example.

          But, I would’t encourage to use that definiton in “real world use” code.

          2. Should I return either EXIT_SUCCESS or EXIT_FAILURE?

          This macros are a highly portable way to tell if the program ran successful.
          They are more descriptive, thus better to read.
          But, it requires a standard C Library, like GLibC or a stub on the host!
          Returning zero from main() does not have to return zero to the host environment.
          On OpenVMS, exit(1) indicates success (odd values and failures by even values).
          https://en.wikipedia.org/wiki/Exit_status#OpenVMS

          3. There is no standard correspondence between the process’ termination status (echo $?) and the program’s exit status, returned by main.
          From the perspetive of a Linux Kernel Developer a process is a program in execution. It can be terminated through signals or whatever, not finishing; plus it might actually return fine, even if it did bad or buggy.
          “Apart from the macros EXIT_SUCCESS and EXIT_FAILURE, the C standard does not define the meaning of return codes. Rules for the use of return codes vary on different platforms.”
          https://en.wikipedia.org/wiki/Exit_status#C_language

          If the Password program would fail due to segfault or would receive Signals like kill or Hangup (HUP) it would leave a value !=0 also.
          That saying, I wouldn’t use the example program to control health-care technique nor to control a tactical aircraft.

          Questions and Article concluding thoughts:
          ==========================================
          It is an article to learn something about Linux/GNU and C, and that is what we did and still do, right?
          The programs name return is actually a disadvantageous name for an example program also, return is a bash builtin command.
          See $ help return and Reserved Exit Codes for Bash:
          http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF

          My original answer had like 400 Lines more, but it is too complex to finish it in a couple of hours. I still might work on it, cause I think modern Documentation about such kind of topics are either loosely spreaded over the internet or often too abstract and out of context. Many modern C Books are just copied and pasted, or only slightly overhauled. There are a dozens of really valueable sources like: “programming from ground up”, “ksplice blog”, “C-FAQ”.

          I am working on a beginners and intermediate cheat-sheet also, covering most topics to advance and for every day use. Beginners Cheat is nearlly finished. Let me know if you are interested.

          One final advice: In many Books on C, a symbolic Debugger isn’t even mentioned. To learn C, the standarized library/subprogram collection is completely overrated. Instead of obstinately reading 100 pages and more which functions exist in the StLib, it is way better to spend time with a symbolic Debugger. If you use a symbolic Debugger as gdb and compile your code with -ggdb3 it can even resolve macros. And all those asserts and printf’s aren’t necessary for learn coding on your machine. Try “The Art of Debugging” by Norman Matloff & Peter Jay Salzman.

          Start with simple examples, do not try to understand GLibC in the beginning, it is highly portable and thus very complex code. Try to avoid most Cross-Platform stuff in the beginning, all those IF-DEF’s and other stuff will surely drive you nuts.

          Regards

          Reply

Leave a Reply

Your email address will not be published. Required fields are marked *