Why does flowing off the end of a non-void function without returning a value not produce a compiler error?

Viewed 75004

Ever since I realized many years ago, that this doesn't produce an error by default (in GCC at least), I've always wondered why?

I understand that you can issue compiler flags to produce a warning, but shouldn't it always be an error? Why does it make sense for a non-void function not returning a value to be valid?

An example as requested in the comments:

#include <stdio.h>
int stringSize()
{
}

int main()
{
    char cstring[5];
    printf( "the last char is: %c\n", cstring[stringSize()-1] ); 
    return 0;
}

...compiles.

11 Answers

C and C++ have different rules.


The language rule in C is that if the closing } of a function that returns a non-void value is reached and the caller attempts to use that value, the behavior is undefined. Just falling off the end of the function has well defined behavior as long as the caller doesn't use the value.

It would be possible to require all possible control paths to execute a return statement before leaving the function, but C traditionally has not required compilers to do that kind of code analysis. (Many compilers will do that analysis anyway and issue a warning if appropriate.)

The main reason for allowing falling off the end of a non-void function is historical. K&R C (the version described in the 1978 first edition of Kernighan and Ritchie's book, before the 1989 ANSI and 1990 ISO C standard) did not have the void keyword or type. And prior to the 1999 ISO C standard, C had the "implicit int" rule, meaning that you could declare or define a function without an explicit return type and it would return an int result.

In K&R C, if you wanted a function that didn't return a result, you would define it without an explicit return type and simply not return a value:

#include <stdio.h>

do_something() {
    printf("Not returning a value\n");
}

int main() {
    do_something();
    return 0;
}

The function would actually return some garbage int value which the caller would quietly ignore.

In modern C, you would write:

#include <stdio.h>

void do_something(void) {
    printf("Not returning a value\n");
}

int main(void) {
    do_something();
}

which guarantees that the caller can't try to use the returned value. As of C89/C90, the language still supported the old style to avoid breaking existing code. When the implicit int rule was dropped in C99, the requirements on non-void functions failing to return a value were not changed (and most C99 and later compilers still support the implicit int rule by default, probably with a warning, so old K&R C code can still be compiled).


In C++, flowing off the end of a function other than a constructor, a destructor, a void function, or main results in undefined behavior, regardless of what the caller tries to do with the result.

In some limited and rare cases, flowing off the end of a non-void function without returning a value could be useful. Like the following MSVC-specific code:

double pi()
{
    __asm fldpi
}

This function returns pi using x86 assembly. Unlike assembly in GCC, I know of no way to use return to do this without involving overhead in the result.

As far as I know, mainstream C++ compilers should emit at least warnings for apparently invalid code. If I make the body of pi() empty, GCC/Clang will report a warning, and MSVC will report an error.

People mentioned exceptions and exit in some answers. Those are not valid reasons. Either throwing an exception, or calling exit, will not make the function execution flow off the end. And the compilers know it: writing a throw statement or calling exit in the empty body of pi() will stop any warnings or errors from a compiler.

I was getting that warning because i forgot to add the statement itr = itr ->currentNode; basically missing that statement, function get into infinte loop and was never returning a value, That's the reason i was getting that warning during the compilation time

void* list_get(list* lst, int idx){

    node* itr = lst->head;
    
    if (idx >= lst->size){
        printf("list out of index");
        exit(1);
    }
    
    while(itr != NULL){
        if(itr->index == idx){
           return  itr->element;
        }
        itr = itr->currentNode;
     
    }


}
Related