I was trying to answer this question. As suggested by the accepted answer, the problem with that code is that not all control paths are returning a value. I tried this code on the VC9 compiler and it gave me a warning about the same. My question is why is just a warning and not an error? Also, in case the path which doesn't return a value gets executed, what will be returned by the function (It has to return something) ? Is it just whatever is there on top of the stack or is the dreaded undefined behavior again?
-
3Professionally most places consider warnings as errors. Consequently most compilers have a flag that tells the compiler to treat warnings as errors. I have never had a job (apart from one working on legacy code) where we did not turn this flag on and insist on 0 warnings before checkin. – Martin York Nov 14 '09 at 19:00
-
2And 0 errors, just in case thought that was not implied. – Martin York Nov 14 '09 at 19:01
7 Answers
Failing to return a value from a function that has a non-void
return type results in undefined behaviour, but is not a semantic error.
The reason for this, as far as I can determine, is largely historical.
C originally didn't have void
and implicit int
meant that most functions returned an int
unless explicitly declared to return something else even if there was no intention to use the return value.
This means that a lot of functions returned an int but without explicitly setting a return value, but that was OK becase the callers would never use the return value for these functions.
Some functions did return a value, but used the implicit int
because int
was a suitable return type.
This means that pre-void
code had lots of functions which nominally returned int
but which could be declared to return void
and lots of other functions that should return an int
with no clear way to tell the difference. Enforcing return
on all code paths of all non-void
functions at any stage would break legacy code.
There is also the argument that some code paths in a function may be unreachable but this may not be easy to determine from a simple static analysis so why enforce an unnecessary return
?

- 755,051
- 104
- 632
- 656
-
2The static analysis can be easily explained if you think of `switch`, while you are strongly advised to provide a `default`, it is not required, and may not be necessary... but how is the compiler supposed to know it? On the other hand... you should treat warnings as errors while compiling, it's better. – Matthieu M. Nov 14 '09 at 20:51
-
It is a very useful warning when it really is a bug and you forgot the return, one that has caught me out several times and warnings as errors at least for this particular one ensures you don't overlook it. – CashCow Sep 13 '12 at 14:30
I would guess it is only a warning because the compiler cannot always be 100% sure it is possible to not hit a return.
i.e. if you had:
-= source1.c =-
int func()
{
if(doSomething())
{
return 0;
}
}
-= source2.c =-
int doSomething()
{
return 1;
}
The compiler in this case might not be able to know it will always hit the return, but you do. Of course this is terrible programming practice to rely on knowing how external code works.
As for what will actually be returned it depends on the platform. On x86 ABIs EAX is used for the return value (up to 32bits) so it will return what ever was placed in that register (which could be a return from something else, a temporary value or total garbage).

- 13,028
- 1
- 32
- 34
-
1In this case why you even have an if statement? This code is just wrong and it should fail to compile. It's a warning because of historical reasons, backward compatibility and stuff. Now C++ should drop this and handle it as error. – Melkon Dec 12 '15 at 11:55
Technically it is not guaranteed to be an error if you call a function and that function always throws an exception. For example here is some pseudo code, and you know raiseError always throws.
MyClass func( params )
{
if( allIsValid() )
{
return myObject;
}
else
{
raiseError( errorInfo );
}
}
If the compiler cannot see the implementation of raiseError, it will not know that the function is going to throw. So really there is actually no undefined behaviour here. Of course it is good to silence the compiler here, which you can do with either writing a "dummy" return statement after raiseError, or a dummy "throw". I call them "dummy" because they will never be reached in reality. (You can also suppress the warning if you really insist). However there is no error or undefined behaviour.

- 30,981
- 5
- 61
- 92
here is another reason it isn't an error
the following will give you the same warning since the compiler expects you to return something from the catch block even though you're throwing there
int foo(){
try{
return bar(0);
} catch(std::exception& ex){
//do cleanup
throw ex;
}
}
int bar(unsigned int i){
if(i == 0){
throw std::string("Value must be greater than 0");
} else{
return 0;
}
}

- 3,527
- 33
- 39
-
It does not expect you to return if you are throwing - at least VC9 doesn't, I don't know about other compilers. However if you are calling a function that always throws (on purpose of course, you have a routine that compiles error messages and throws) and you call it and the compiler can't "see" it, you get the warning. Thus my solution below. – CashCow Sep 13 '12 at 14:28
Another example where it may be okay for some control paths to not return a value:
enum E : int {A, B};
int foo(E e) {
switch (e) {
case A: return 30;
case B: return 50;
}
}
It's possible that e
won't be A
or B
, but the implication is that it always will be one of those values. If that's the case then the code is fine and there's no problem. Making this warning into a mandatory error would require unnecessary, 'unreachable' clutter.
If you want the warning to be an error anyway, you can configure your compiler to do that with a flag like /WX or -Werror. Though of course you should note that different compilers may make different determinations as to what's unreachable so you may be fixing different things for different compilers.

- 86,085
- 15
- 179
- 244
Consider the following scenario:
UINT GenderID(GENDER gender)
{
switch(gender)
{
case MALE:
return MALE_ID;
case FEMALE:
return FEMALE_ID;
}
// default not required because [GENDER] in our 'Matrix' CAN be either M or F
}
a C++ complier should let you have your 'Matrix' your way; Thus its not an Error.

- 4,908
- 4
- 37
- 54
-
You're basically saying its not an error because you came up with a contrived example you believe should work. – Andy Dec 31 '12 at 15:54
-
@Andy as **it is not an error** - i don't have to prove it. Besides my example is very much a real world one - and the point of it is to present a situation where - to declare it an error would be an error itself. – Ujjwal Singh Dec 31 '12 at 16:20
-
1You do not answer *why* it is allowed in the first place, which is what the OP wants to know. Your answer is a tautology and only provides another example of what the OP is asking about, it does not answer the question. The only thing I can glean from your answer statement is that you think the code should compile because it should 'let you have it your way.' – Andy Dec 31 '12 at 16:38
-
@Andy sir, i believe - that is the answer: 'the compiler **should** let you have it your way'; it can warn though - which it does. – Ujjwal Singh Dec 31 '12 at 18:41
-
1Now you're talking about your opinion. Code written in C# without a return (when the method is not void) generates a compiler error. IMO, the usefulness of undefined return values are far outweighed by the errors it causes in programs, and I would never let such code pass a code review. – Andy Dec 31 '12 at 20:53
-
C & C++ codes sometimes have to run on very tight hardware constraints. Unnecessary Overheads are not welcome in C/C++ world unlike C#. Perhaps you are right in your opinion and that's why a warning is raised. But making it mandatory - by the compiler - would be wrong. – Ujjwal Singh Dec 31 '12 at 21:08
It is not an error because it may be the intended behaviour. For example, some encryption libraries use uninitialized local data as a step for seeding. As return values are kept in calling-convention and platform specific locations, this may help in some unusual (like the above) situations. In this case, the function returns whatever is left on the register used to return the return value.

- 2,522
- 2
- 26
- 32
-
1Can you cite a specific example of an encryption library using uninitialized data as seeding? It sounds highly unlikely that a competent library would do that as the uninitialized data is anything but random in cryptographic terms. – Jonathan Leffler Nov 14 '09 at 18:39
-
Really? What encryption libraries do that? Relying on undefined behavior in an encryption library seems like a big potential security hole. – Jeremy Friesner Nov 14 '09 at 18:41
-
I would any day prefer a properly generated random number rather than uninitialized data as a seed. If you use MSVC++ debugger, you would regularly see that uninitialized data have fixed values (in debug version of program 0xCCCCCCCC, 0xCDCDCDCD). – Shailesh Kumar Nov 14 '09 at 18:59
-
1@Shailesh: I am not a fan of uninitialized data structures. I just remembered that, there was an occasion some programmers relied on this thing... – Malkocoglu Nov 14 '09 at 21:11
-
@Shailesh: Unfortunately, it is very hard to have properly generated random numbers (http://www.springerlink.com/content/kkpghbg30r1xce4m/). I also remember some cryptographer attacking the Intel hardware RNG on their chipsets by using some other hardware on that chip (maybe the sound card, i do not remember) so that he can keep the temperature of that area high enough that the RNG produced little entropy (as it relied on thermal noise)... – Malkocoglu Nov 14 '09 at 21:20
-
We are not safe even with hardware (True, they say) RNG systems. http://www.lightbluetouchpaper.org/2009/09/08/tuning-in-to-random-numbers/ – Malkocoglu Nov 14 '09 at 21:35
-
0xCCCCCCCC and 0xCDCDCDCD are valid numbers and valid data that you might return. For a debug compiler to detect memory as uninitialised or deleted, it has to allocate extra to store this information, possibly in bytes like that. Usually this is done with dynamically allocated memory where it returns you a pointer but has every right to allocate more and write its own data in a prefix. – CashCow Sep 13 '12 at 10:20