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

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: %cn", cstring[stringSize()-1] ); 
    return 0;
}

…compiles.

Enquirer: Catskul

||

Solution #1:

C99 and C++ standards don’t require functions to return a value. The missing return statement in a value-returning function will be defined (to return 0) only in the main function.

The rationale includes that checking if every code path returns a value is quite difficult, and a return value could be set with embedded assembler or other tricky methods.

From C++11 draft:

§ 6.6.3/2

Flowing off the end of a function […] results in undefined behavior in a value-returning function.

§ 3.6.1/5

If control reaches the end of main without encountering a return statement, the effect is that of executing

return 0;

Note that the behaviour described in C++ 6.6.3/2 is not the same in C.


gcc will give you a warning if you call it with -Wreturn-type option.

-Wreturn-type Warn whenever a function is defined with a return-type that
defaults to int. Also warn about any
return statement with no return-value
in a function whose return-type is not
void (falling off the end of the
function body is considered returning
without a value), and about a return
statement with an expression in a
function whose return-type is void.

This warning is enabled by -Wall.


Just as a curiosity, look what this code does:

#include <iostream>

int foo() {
   int a = 5;
   int b = a + 1;
}

int main() { std::cout << foo() << std::endl; } // may print 6

This code has formally undefined behaviour, and in practice it’s calling convention and architecture dependent. On one particular system, with one particular compiler, the return value is the result of last expression evaluation, stored in the eax register of that system’s processor.

Solution #2:

gcc does not by default check that all code paths return a value because in general this cannot be done. It assumes you know what you are doing. Consider a common example using enumerations:

Color getColor(Suit suit) {
    switch (suit) {
        case HEARTS: case DIAMONDS: return RED;
        case SPADES: case CLUBS:    return BLACK;
    }

    // Error, no return?
}

You the programmer know that, barring a bug, this method always returns a color. gcc trusts that you know what you are doing so it doesn’t force you to put a return at the bottom of the function.

javac, on the other hand, tries to verify that all code paths return a value and throws an error if it cannot prove that they all do. This error is mandated by the Java language specification. Note that sometimes it is wrong and you have to put in an unnecessary return statement.

char getChoice() {
    int ch = read();

    if (ch == -1 || ch == 'q') {
        System.exit(0);
    }
    else {
        return (char) ch;
    }

    // Cannot reach here, but still an error.
}

It’s a philosophical difference. C and C++ are more permissive and trusting languages than Java or C# and so some errors in the newer languages are warnings in C/C++ and some warnings are ignored or off by default.

Respondent: John Kugelman

Solution #3:

You mean, why flowing off the end of a value-returning function (i.e. exiting without an explicit return) is not an error?

Firstly, in C whether a function returns something meaningful or not is only critical when the executing code actually uses the returned value. Maybe the language didn’t want to force you to return anything when you know that you are not going to use it anyway most of the time.

Secondly, apparently the language specification did not want to force the compiler authors to detect and verify all possible control paths for the presence of an explicit return (although in many cases this is not that difficult to do). Also, some control paths might lead into to non-returning functions – the trait that is generally non known to the compiler. Such paths can become a source of annoying false positives.

Note also, that C and C++ differ in their definitions of the behavior in this case. In C++ just flowing off the end of a value returning function is always undefined behavior (regardless of whether the function’s result is used by the calling code). In C this causes undefined behavior only if the calling code tries to use the returned value.

Respondent: AnT

Solution #4:

It is legal under C/C++ to not return from a function that claims to return something. There are a number of use cases, such as calling exit(-1), or a function that calls it or throws an exception.

The compiler is not going to reject legal C++ even if it leads to UB if you are asking it not to. In particular, you are asking for no warnings to be generated. (Gcc still turns on some by default, but when added those seem to align with new features not new warnings for old features)

Changing the default no-arg gcc to emit some warnings could be a breaking change for existing scripts or make systems. Well designed ones either -Wall and deal with warnings, or toggle individual warnings.

Learning to use a C++ tool chain is a barrier to learning to be a C++ programmer, but C++ tool chains are typically written by and for experts.

Solution #5:

I believe this is because of legacy code (C never required return statement so did C++). There is probably huge code base relying on that “feature”. But at least there is -Werror=return-type
flag on many compilers (including gcc and clang).

Respondent: d.lozinski

Solution #6:

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.

Respondent: Yongwei Wu

Solution #7:

Under what circumstances doesn’t it produce an error? If it declares a return type and doesn’t return something, it sounds like an error to me.

The one exception I can think of is the main() function, which doesn’t need a return statement at all (at least in C++; I don’t have either of the C standards handy). If there is no return, it will act as if return 0; is the last statement.

Respondent: David Thornley

Solution #8:

Sounds like you need to turn up your compiler warnings:

$ gcc -Wall -Wextra -Werror -x c -
int main(void) { return; }
cc1: warnings being treated as errors
<stdin>: In function ‘main’:
<stdin>:1: warning: ‘return’ with no value, in function returning non-void
<stdin>:1: warning: control reaches end of non-void function
$
Respondent: Chris Lutz

Solution #9:

It is a constraint violation in c99, but not in c89. Contrast:

c89:

3.6.6.4 The return statement

Constraints

A return statement with an expression shall not appear in a
function whose return type is void .

c99:

6.8.6.4 The return statement

Constraints

A return statement with an expression shall not appear in a function whose return type is void. A return statement without an expression shall only appear in a function whose return type is void.

Even in --std=c99 mode, gcc will only throw a warning (although without needing to enable additional -W flags, as is required by default or in c89/90).

Edit to add that in c89, “reaching the } that terminates a function is equivalent to
executing a return statement without an expression” (3.6.6.4). However, in c99 the behavior is undefined (6.9.1).

Respondent: mbauman

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.