6

I have successfully compiled and executed the following code in GCC:

#include <stdio.h>

int foo()
{

}

int main()
{
    int i = 12345;
    i = foo();
    printf("i is: %d", i);
}

The output is:

i is: 0

So GCC allowed me not to return from the function foo() and made foo() return 0.

Does this behavior only apply to GCC or do other C standards also have it (based on my understanding, GCC does not conform to any C standard)?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user8426277
  • 587
  • 3
  • 17
  • 4
    Compiler designers have two choices. Either reject some (valid) code because the compiler cannot *prove* it returns a value even though it does. OR allow code which never to returns a value to compile anyway. It's impossible to catch this error perfectly (see the halting problem https://en.wikipedia.org/wiki/Halting_problem) – Philip Couling Jun 15 '18 at 12:38
  • 3
    Try `gcc -Wall`. – Steve Summit Jun 15 '18 at 12:38
  • @SteveSummit I didn't manage to get the warning go away no matter which options I excluded. I think `-Wreturn-type` is part of the default warnings. I compiled with nothing but `gcc -std=c11` and got the warning. – Lundin Jun 15 '18 at 12:44
  • 1
    @AnttiHaapala The [linked dupe](https://stackoverflow.com/questions/10858391/why-do-programs-in-c-compile-even-when-the-return-statement-is-missing) was much worse than this one, without a single answer quoting the standard as requested by the OP. Better to close that old one as a dupe to this than vice versa. I re-opened this. – Lundin Jun 15 '18 at 12:54
  • (Someone else with C dupe hammer kindly settle the issue. I would close [this](https://stackoverflow.com/questions/10858391/why-do-programs-in-c-compile-even-when-the-return-statement-is-missing) as dupe to this question here, but I'm partial since I posted an answer here) – Lundin Jun 15 '18 at 12:56
  • 1
    @Lundin Agree that the answers at the proposed dup are poor, and the ones here are much better. – Steve Summit Jun 15 '18 at 13:20
  • @Lundin I was surprised, but on my system neither `cc` (which is Apple LLVM version 6.0) nor `gcc` (4.6.3) seems to have `-Wreturn-type` turned on by default; that's why I suggested `-Wall`. – Steve Summit Jun 15 '18 at 13:23
  • Yuck ok so this is some gcc inconsistency. I get the warning rather arbitrary in some versions, newer versions don't give it. clang seems to always give it no matter version. – Lundin Jun 15 '18 at 13:28

5 Answers5

10

None of the C standards require the compiler to produce an error or warning if a function is missing a return statement¹.

In all of the C standards the behaviour is undefined if control flow reaches the end of non-void function without a return and the return value of the function is then used (as in your code).

So if by "allow" you mean "specify it as legal, well-defined behaviour", none of the C standards allow your code. If you just mean "don't require an implementation to produce an error", all of them do.

Do note that the value of i in your example program can easily change if you change the definition of foo (without adding a return). You can not rely on it being 0. There is no rule that says "if there's no return, return 0" - neither in the standard nor in GCC's implementation. According to the standard it's undefined and in GCC it will just be whatever happens to be in the return register at that time.


¹ In the general case this would be undecidable. One could do what e.g. Java does and define the rules so that some otherwise valid function definitions are rejected, but none of the C standards do this.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 1
    No, the behaviour is not undefined if control flow reaches the end of non-void function without return; it is undefined only if the return value is used. – Antti Haapala -- Слава Україні Jun 15 '18 at 12:51
  • @AnttiHaapala You're right, I've added that condition. – sepp2k Jun 15 '18 at 12:53
  • It might be useful to provide an example of code which is impossible or difficult for the compiler to analyse (eg. switch without default in which the controlling expression is somehow known to match some case label.) It is certainly true that "in the general case thus is undecidable" but many readers would probably see that more clearly with a concrete case. – rici Jun 15 '18 at 15:59
  • There's also a special exception for `main` -- if control flow reaches the end of `main` without a return statement, it is as if it returns 0. – Chris Dodd May 06 '19 at 20:29
8

gcc does give a warning:

warning: control reaches end of non-void function [-Wreturn-type]|

(Disclaimer: I get different results from different versions of gcc. To be sure, compile with -Wall -Wextra -pedantic-errors.)

What happens in this case is not covered by the C standard. The standard merely says that (C11 6.9.1/12):

If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.

In your case, the caller does use the return value by storing it in i. So this is undefined behavior - the behavior is not covered by the C standard and the compiler has no obligation to inform you about it. Anything could happen. So this should be regarded as a bug in most cases.

Lundin
  • 195,001
  • 40
  • 254
  • 396
5

Well, there is nothing in the standard that stops you from writing illegal code. However, it does mention that this is undefined behaviour.

Quoting C11, chapter §6.9.1:

If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
2

C89 states (at least in the draft):

If a return statement without an expression is executed, and the value of the function call is used by the caller, the behavior is undefined.

Which means it's legal to write code as you did. It's not an error. It's undefined behavior, but it's syntactically legal. Since it's not specifically "illegal" and syntactically allowed a compiler adhering to C89 may very well just compile it and the result will be undefined... but it's a legit program as far as the compiler is concerned.

That's why even with -Wpedantic it's probably not going to be rejected. Undefined behaviour means exactly what it says: it's undefined what happens. Be aware that compilers may optimize undefined behaviour away completely (which is ok, because it's undefined behaviour anyway, so the compiler can do whatever it wants).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mroman
  • 1,354
  • 9
  • 14
0

I think that in this case the undefined behaviour is pretty common between the most popular compilers. If you disassemble your foo function, you will find these instructions in almost every version of GCC:

    push    rbp
    mov     rbp, rsp
    nop
    pop     rbp
    ret

This code is doing nothing, exactly what you want. However, it is not even following the calling convention rules whenever a result from a function is expected. The result should be passed to the caller by the eax/rax register. In foo, this register is not even modified and this is the reason why you get undefined behaviour.


Omitting a return is like saying: "I do not want to follow the rules you gave me". Clang produces similar code and I guess other compilers (unless further options or optimizations are applied) reason in the same way. Nothing stops you from doing that, neither a compiler. Undefined behaviour is the price for this freedom.


The proof that the compiler puts its faith on the code written is that the eax/rax register is really considered as the store for the function's result. In your case eax is never used before in that code, and its last value is zero. But try with this:

int i = 12345;
int m = 12345;
m *= 3;
i = foo();
printf("i is: %d", i);

The output is probably going to be 37035 even if that's the m's value.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42