Thread: No return value

  1. #1
    Registered User
    Join Date
    Dec 2006
    Posts
    69

    No return value

    I'd like to know what the C standard says about the following scenario:

    I have a function that returns an int:
    Code:
    int myFunc()
    {
       // Note that there is no return statement..
    }
    If I call this function, and store the return value:
    Code:
    int x = myFunc();
    What value should x contain? Is this specified to always be 0, or some value, or may it be random garbage?

  2. #2
    Registered User
    Join Date
    Sep 2006
    Posts
    835
    A conforming compiler wouldn't let you compile that code in the first place, since it doesn't have the return value its declaration says it does.

    Edit: There are the exceptions that

    1) main() returns int, but in C90 an explicit return isn't required, in which case the return value is undefined - which one should avoid by using an explicit "return 0;" at the closing brace. (In C99, if there is no explicit return, then main() returns 0 automatically at the closing brace, so there is always a well-defined return value.)

    2) A function returning void (no return value) doesn't need an explicit return, since it returns automatically at the closing brace (although one is needed for an early return, of course).
    Last edited by robatino; 08-11-2007 at 10:04 AM.

  3. #3
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    I've written some proof of concept programs and have some posts on this subject with regard to the x86 architecture and how values are passed back, and related to it, why it's a bad idea to declare main() as returning void.

    For the x86 architecture, the CPU has physical storage places for computation and data manipulation called registers. Various registers serve various purposes. Most operations cannot be performed directly on values in computer memory, and these values must be moved from RAM to a CPU register. C hides a lot of this aspect of computer programs because C was meant to be more portable than dealing with assembly language and the machine architecture. It's pretty fairly certain you'll have RAM of some sort in your program no matter what architecture. It's not certain at all what registers you may or may not have and how you can use them. Therefore, C sticks to dealing with variables as if they are in computer memory all the time and the aspect of moving them around and getting them in the right positions inside the CPU and back again is left to the compiler.

    Still sticking with the x86 architecture, there are four general-purpose registers that support math operations. They are named EAX, EBX, ECX, and EDX. The general way function values are returned is by writing the return value into the EAX register at the end of the function. The calling function then reads in that value.

    Now here's an example of what could happen should this code actually work:

    Code:
    int myFunc()
    {
       printf("Bleh\n");
       // Note that there is no return statement..
    }
    
    ...
    
    int x = myFunc();
    Should this actually compile, x should contain the number 5. Why? Because printf() returns an int. printf()'s return value is the number of chars printed. In this case it would be, 'B', 'l', 'e', 'h', and '\n'. Since myFunc() doesn't do anything else the return value of 5 is left in the EAX register. myFunc() leaves EAX alone and just returns leaving what was in there previously. When control returns back from myFunc(), the value in EAX is assumed to be the return value, and x is assigned the number 5.

    Proof of concept (which may or may not work for you):

    Code:
    #include <stdio.h>
    
    int myFunc(void);
    
    int main(void)
    {
    	int x = myFunc();
    	
    	printf("x = &#37;d\n", x);
    	
    	return 0;
    }
    
    int myFunc(void)
    {
    	printf("Bleh\n");
    }
    Compiling with MinGW on an x86 system, I received the following output:

    Code:
    Bleh
    x = 5


    Now with that said, do not try to make assumptions about return values. Always explicitly return values, even in the case of C99 and C++ that supports leaving the return main() (because it does it automatically), if you'd like to be proactive in catching errors (although those cases it's not necessary). It's best to always make sure your program is well defined. Once you get into the area of undefined behavior..... you should not expect your program to work.
    Last edited by MacGyver; 08-11-2007 at 09:55 PM.

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    835
    I just tested your code with gcc and contrary to my expectation, even with options "-W -Wall -ansi -pedantic", gcc STILL allows it to compile (though -Wall triggers "warning: control reaches end of non-void function"). So compilers are a lot dumber than I thought they were - strange since it seems pretty trivial to check a function for a valid return statement if needed. And I got the same output you did.

    Edit: Having said that, I prefer to use the default return in main() in C++, since it always does the right thing (if one puts in the closing return manually, one might put in something like "return 1;" by mistake), and it's one less line of code.

    Edit: Checking that each return statement in a given function has the right return type is completely mechanical. However, there's no way to determine reliably whether the closing brace is ever reached - if not, a return statement there wouldn't be needed. Maybe that's why it doesn't check.
    Last edited by robatino; 08-11-2007 at 10:45 PM.

  5. #5
    The larch
    Join Date
    May 2006
    Posts
    3,573
    You did get a warning that is complaining about a missing return statement?

    I didn't look it up in the standard, but it is probably undefined or implementation-defined what should happen if nothing is implicitly returned. If it is so, compilers cannot report this as an error: it's syntactically legal, but probably indicates that your semantics have logical problems.

    I think it is not too hard for compilers to spot missing returns as well as unreachable code, but of course, it is quite possible that an else branch is never supposed to be reached because of the pre-conditions of the function.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677

    Post

    First of all, I quite like "-Werror", which means that all warnings are treated as errors. The Xen project I've been working on for the last couple of years use this switch. It means that you can not get your code running until you've fixed any warnings - a good thing almost all the time.

    Second, if we have a piece of code lihe this:
    Code:
    something *find_sthing(int id) {
       something *p;
       for(p = head; p != NULL; p = p->next) {
          if (p->id == id) 
            return p;
      }
    }
    The compiler can't know that the item we're searching for is guaranteed to be in the linked list - we may know that, so the above code works OK, but the compiler will complain that the code may reach end of function without a return statement.

    Naturally, it would be a good thing to put a "catch" for the code getting through the loop without finding something (perhaps the linked list got corrupted by some bad pointer usage, or some such).

    --
    Mats

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    835
    > I think it is not too hard for compilers to spot missing returns as well as unreachable code, but of course, it is
    > quite possible that an else branch is never supposed to be reached because of the pre-conditions of the function.

    One can write a program where whether the closing brace is reached depends on some unsolved mathematical problem, so it can only be done heuristically, which makes using a warning instead of an error safer, in case the heuristics fail.

    > First of all, I quite like "-Werror", which means that all warnings are treated as errors. The Xen project I've
    > been working on for the last couple of years use this switch. It means that you can not get your code running
    > until you've fixed any warnings - a good thing almost all the time.

    I have a piece of code where until recently, gcc gave me a warning about a variable possibly being used before being initialized (checking just now, the warning is gone, so maybe the heuristics have improved in the latest version - I upgraded from 4.1.1 to 4.1.2 recently). In fact, I know this is impossible. I could have suppressed the warning by doing an unnecessary initialization to 0. It's not a performance issue, I just don't like hacking my code to hide the fact that the compiler's heuristics were off.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Another weird error
    By rwmarsh in forum Game Programming
    Replies: 4
    Last Post: 09-24-2006, 10:00 PM
  2. Why only 32x32? (OpenGL) [Please help]
    By Queatrix in forum Game Programming
    Replies: 2
    Last Post: 01-23-2006, 02:39 PM
  3. OpenGL Window
    By Morgul in forum Game Programming
    Replies: 1
    Last Post: 05-15-2005, 12:34 PM
  4. opengl help
    By heat511 in forum Game Programming
    Replies: 4
    Last Post: 04-05-2004, 01:08 AM
  5. Algorithm to walk through a maze.
    By Nutshell in forum C Programming
    Replies: 30
    Last Post: 01-21-2002, 01:54 AM