tags:

views:

1471

answers:

8

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 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.

A: 

It's a compile error in MSVC++, are you sure gcc allows this?

edit: the standard (appears) to say the result of doing this is undefined.

Martin Beckett
gcc issues a warning, not an error.
Jefromi
@Jefromi - GCC issues nothing for this by default. Very few warnings are on by default in GCC.
Chris Lutz
@Chris Lutz: maybe we are using different versions, but I definitely tested this before posting - `gcc -o foo foo.c`, with foo.c containing `void foo() { return 1; }`
Jefromi
@Jefromi - That's the opposite of this question, though that should also be an error. Try `int main(void) { return; }` (my GCC version is 4.0)
Chris Lutz
Sorry, my bad. I don't know how I misread the question so many times.
Jefromi
It's alright. It happens to the best of us.
Chris Lutz
A: 

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.

David Thornley
`main()` needs a `return` in C.
Chris Lutz
@Jefromi: The OP is asking about a non-void function without a `return <value>;` statement
Bill
main automatically returns 0 in C and C++. C89 needs an explicit return.
Johannes Schaub - litb
@Chris: in C99 there's an implicit `return 0;` at the end of `main()` (and `main()` only). But it's good style to add `return 0;` anyway.
pmg
+2  A: 

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, maybe 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 this is not difficult to do).

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.

AndreyT
+1 but can't C++ omit `return` statements from the end of `main()`?
Chris Lutz
@Chris Lutz: Yes, `main` is special in that regard.
AndreyT
A: 

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
$
Chris Lutz
Saying "turn on -Werror" is a non-answer. Clearly there is a difference in severity between issues classified as warnings and errors, and gcc treats this one as the less severe class.
Jefromi
@Jefromi - I said turn up warnings, and I'd say most of `-Wall` _should_ probably be errors anyway (especially this case). But the reason I had all three of those in there is because that's what I have `gcc` aliased to on my system.
Chris Lutz
@Jefromi: From the pure language point of view, there's no difference between warnings and errors. The compiler is only required to issue a "disgnostic message". There's no requirement to stop compilation or call something "an eror" and something else "a warning". One a diagnostic message is issued (or any kind), it is entirely up to you to make a decision.
AndreyT
@Chris - ah, okay. And I do tend to agree with you about the warnings really being errors. I myself compile with -Werror whenever I can - it's hard when you're not responsible for all the code, though.
Jefromi
Then again, the issue in question causes UB. Compilers are not required to catch UB at all.
AndreyT
@Andrey: I wasn't trying to speak from a pure language point of view. Compilers simply try to help you make that decision by choosing a single word based on how severe the issue probably is, and given that fact, we can ask the question of which word should be chosen for a particular issue, yes?
Jefromi
@Andrey - According to pmg's answer, it's a constraint violation, and must therefore issue a diagnostic.
Chris Lutz
Why does this have two downvotes?
Chris Lutz
I didn't downvote, but the others probably did because the original question contains the quote "I understand that you can issue compiler flags to produce a warning, but shouldn't it always be an error?"
Catskul
@Chris: I think people are expecting an answer of the form "gcc does what it does because ___" not "gcc can be made to do the right thing by ___". You've got my +1, though!
Jefromi
In 6.9.1/12 in n1256 it says "If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined."
Johannes Schaub - litb
@Chris Lutz: I don't see it. It is a constraint violation to use an *explicit* empty `return;` in a non-void function, and it is a constraint violation to use `return <value>;` in a void function. But that's, I believe, not the topic. The OP, as I understood it, is about exiting a non-void function without a `return` (just allowing control to flow off the end of the function). It is not a constraint violation, AFAIK. The standard just says it is always UB in C++ and sometimes UB in C.
AndreyT
@AndreyT - Ah. Before the code example, it said "return from non-void function without returning a value" which I took to mean an explicitly empty return.
Chris Lutz
A: 

In C it's a constraint violation. A conforming compiler must emit a diagnostic when trying to compile code that returns something from a void function or returns nothing from a non-void function.

6.8.6.4 The return statement
Constraints
1 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.

pmg
5.1.1.3 Diagnostics $1 A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.
pmg
Yeah, I removed my comment when AndreyT agreed with you, assuming it was in some other part of the standard. Nice to know.
Chris Lutz
6.8.6.4 also doesn't say anything about non-void functions without return statements at the end of the block.
james woodyatt
This paragraph does only apply to return statements. Not to cases where you flow off a function body. See 6.9.1/12 for the latter case.
Johannes Schaub - litb
Thanks litb. I have been looking for that for ages!
pmg
Actually, section 6.8.6.4 does apply to the question asked. It just says that a non-void function must not use plain "return" without a parameter. It says nothing about not using return at all.
sleske
+12  A: 

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.

John Kugelman
Nice explanation.
Otis
If javac actually checks code-paths wouldn't it see that you could never reach that point?
Chris Lutz
In the first one it doesn't give you credit for covering all of the enum cases (you need a default case or a return after the switch), and in the second one it doesn't know that `System.exit()` never returns.
John Kugelman
It seems straightforward for javac (an otherwise powerful compiler) to know that `System.exit()` never returns. I looked it up (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#exit%28int%29), and the docs just say it "never normally returns". I wonder what that means...
Paul Biggar
@Paul: it means they didn't have a goodeditor. All other languages say "never returns normally" -- i.e., "doesn't return using the normal return mechanism."
Max Lybbert
A: 

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).

Matt B.
Note that this only covers explicit `return` statements. It doesn't cover falling off the end of a function without returning a value.
John Kugelman
Note that C99 misses "reaching the } that terminates a function is equivalent to executing a return statement without an expression" so it's not made a constraint violation, and thus no diagnostic is required.
Johannes Schaub - litb
+11  A: 

It is allowed by C99 and C++ standards. The return with no value in a value-returning function will be defined (to 0) only in the main function.

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

From n2960 draft:

§ 6.6.3/2

Flowing off the end of a function is equivalent to a return with no value; this 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;

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 (1):

#include <iostream>

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

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

(1) This is calling convention and arquitecture dependent.
(2) The return value is the result of last expression evaluation, stored in eax register.

fnieto
I'll buy the code path checking argument.
Catskul
I'm picking this one because I feel like it's the most concise.
Catskul