Omitting return statement in C++
Omitting the return
statement in a non-void
function [Except main()
] and using the returned value in your code invokes Undefined Behaviour.
ISO C++-98[Section 6.6.3/2]
A return statement with an expression can be used
only in functions returning a value; the value of the expression is
returned to the caller of the function. If required, the expression
is implicitly converted to the return type of the function in which it
appears. A return statement can involve the construction and copy of
a temporary object (class.temporary). 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.
For example
int func()
{
int a=10;
//do something with 'a'
//oops no return statement
}
int main()
{
int p=func();
//using p is dangerous now
//return statement is optional here
}
Generally g++ gives a warning: control reaches end of non-void function
. Try compiling with -Wall
option.
Can I omit return from main in C?
Yes, as of C99, reaching the }
at the end of main returns 0 if the return type of main
is compatible with int
.
5.1.2.2.3 Program termination
If the return type of the
main
function is a type compatible with int, a return from the initial call to themain
function is equivalent to calling theexit
function with the value returned by themain
function as its argument;11) reaching the}
that terminates themain
function returns a value of 0. If the return type is not compatible withint
, the termination status returned to the host environment is unspecified.
What if I omit the return type of main function in C?
C89/90 still has the implicit int rule, so main()
is equivalent to int main()
. By leaving off the return type, you've implicitly defined the return type as int
. Some may consider this sloppy, but it's strictly conforming (i.e., involves no implementation defined, undefined or unspecified behavior).
For C99, the implicit int
rule has been removed, so main()
isn't defined. However, a compiler is only required to "issue a diagnostic" upon encountering a violation of a Shall
or Shall not
clause -- it's still free to continue compiling after issuing the diagnostic. Exactly what constitutes a diagnostic is implementation defined. As such, all that's needed for gcc to conform in this respect is documentation to say that the warning it issues is to be considered a diagnostic.
Edit: "implicit int
" in the C89/90 standard isn't really a single rule in a single place -- it's spread around in a couple of places. The primary one is §6.5.2.1, where it says:
--
int
,signed
,signed int
, or no type specifiers
This is part of a list, where all the items on each line of the list are considered equivalent, so this is saying that (unless otherwise prohibited) lack of a type specifier is equivalent to specifying (signed) int
.
For function parameters, there's a separate specification (at §6.7.1) that: "Any parameter that is not declared has type int
."
C and C++ functions without a return statement
"Why is this still allowed?" It is not, but in general, the compiler cannot prove you are doing it. Consider this (of course extremely simplified) example:
// Input is always true because logic reason
int fun (bool b) {
if (b) {
return 7;
}
}
Or even this one:
int fun (bool b) {
if (b) {
return 7;
}
// Defined in a different translation unit, will always call exit()
foo();
// Now we can never get here, but the compiler cannot know
}
Now the first example could flow off the end, but it never will as long as the function is used "properly"; and the second one could not, but the compiler cannot know this. So the compiler would break "working" and legal, although probably stupid, code by making this an error.
Now the example you posted is a little different: Here, all paths flow off the end, so the compiler could reject or just ignore this function. It would however break real world code that relies on the compiler specific behavior as in your production code, and people do not like that, even if they are wrong.
But in the end, flowing off the end of a non-void
function still is undefined behavior. It might work on certain compilers, but it is not and never was guaranteed to.
Function returns value without return statement
For x86 at least, the return value of this function should be in eax
register. Anything that was there will be considered to be the return value by the caller.
Because eax
is used as return register, it is often used as "scratch" register by callee, because it does not need to be preserved. This means that it's very possible that it will be used as any of local variables. Because both of them are equal at the end, it's more probable that the correct value will be left in eax
.
Omitting return statement in a function recursively calling itself
You are correct, there are missing return
keywords before the recursive calls to get_node_by_val
. In both cases, the function exits without a return
statement. Since it not a void
function, this has undefined behavior.
The code behaves as expected because the register (or whatever other means appropriate for your system's ABI) used for the return value has been set in the recursive call (actually in the final recursive call) and happens do not modified by the caller until it returns to its own caller, which it does immediately. This behavior is not guaranteed and of course it is very bad practice to rely on it.
You should configure the compiler to issue useful warnings to avoid such silly mistakes: gcc -Wall -Wextra -Werror
, clang -Weverything -Werror
...
Is omitting return statement undefined behaviour in C89 (aka ANSI C)?
Years ago I actually debugged issues caused by this. It gets even more interesting if you have some code paths with returns and others without.
As @aruisdante surmised in the comments, the behavior exhibited is indeed 'undefined', but the only undefined part is the value returned (it's not like many other 'undefined' situations that may crash the program).
These days this actually amounts to a security risk because the 'undefined' value returned is generally whatever happens to be in the CPU register normally used to return values (or on the stack in some implementations), which could theoretically be used to leak sensitive data.
Is it valid to omit the return statement of a non-void function template that throw
are example 1 and example 2 valid?
Yes.
Or we have UB/ill-formed.
No.
Is it valid to omit the return statement of a non-void function/function template that throw
Yes. A non-void returning function must either throw or return avalue. It cannot do both at the same time.
So is
int func(){ int x = 4; throw; return x;}
valid
It's valid. But it never returns a value. Everything after the throw is dead code.
implicit return value in C with no explicit return in a function that should return non-void
I compiled with gcc version 4.9.2 on Linux with return-type warning on [-Wreturn-type]
The caller expected a true (in C, interpreted as non-zero) for success and 0 for failure. This is what was happening. Since a function that returns non-void, when called, space is created on the stack for the return value, then context is switched to the callee, and finally, when the control comes back to the caller, context is switched back popping all local variables from stack and keeping the return value there on stack to be popped by the caller after return from callee. Thus, having no explicit return statement caused some value to be returned. That is equivalent to having explicit return statement as ‘return 1;’ or 'return 0;' as appropriate. So, better (of course) would be to have explicit return statement e.g.
int somefn() {
// .. do something ..
if (..somecond..)
return 0; // failure
else
return 1; // success
}
To avoid surprises, I would say, compilers should flag 'no return statement in function returning non-void'
as error.
Related Topics
How to Convert Std::String to Lpcwstr in C++ (Unicode)
What Techniques Can Be Used to Speed Up C++ Compilation Times
How to Get a Process Handle by Its Name in C++
C++ Dll Export: Decorated/Mangled Names
How to Detect Unnecessary #Include Files in a Large C++ Project
When Is a Private Constructor Not a Private Constructor
What Is a Subnormal Floating Point Number
In C++ How to Go to a Specific Line in a Text File
How to Assign an Alias to a Function Name in C++
What Is More Efficient? Using Pow to Square or Just Multiply It With Itself
What Makes More Sense - Char* String or Char *String
How to Concatenate Multiple C++ Strings on One Line
What Are the Different Calling Conventions in C/C++ and What Do Each Mean
How to Emulate Template≪Auto X≫
Visual Studio 2015 "Non-Standard Syntax; Use '&' to Create a Pointer to Member"