In C++, what happens when a function that is supposed to return an object ends without a return statement? What gets returned?
e.g.
std::string func
What gets returned?
We don't know. According to the standard, the behavior is undefined.
§6.6.3/2 The return statement [stmt.return]:
(emphasis mine)
Flowing off the end of a constructor, a destructor, or a function with a cv
void
return type is equivalent to areturn
with no operand. Otherwise, flowing off the end of a function other thanmain
(basic.start.main) results in undefined behavior.
In fact most compilers would give a warning for it, like Clang:
warning: control reaches end of non-void function [-Wreturn-type]
I was curious, so I made a few tests on Visual C++ 2015.
int f()
{
if (false)
return 42;
// oops
}
int main()
{
int i = f();
}
I had to add the if
to get a warning instead of a hard error:
> cl /nologo /FAs /c a.cpp
a.cpp(6) : warning C4715: 'f': not all control paths return a value
The assembly code that's generated is pretty simple and I've removed the irrelevant parts. Here's the meat of f()
:
f:
xor eax, eax
je label
mov eax, 42
label:
ret
The xor
line is basically eax=0
. Because if (false)
is a constant condition, the generated code doesn't even bother to do a comparison and will then jump unconditionally to label
, which just returns from the function. You can see that the "return value" (42
) would actually be stored in eax
, but that this line won't ever be executed. Therefore, eax == 0
.
Here's what main()
does:
call f
mov _i$[ebp], eax
ret
It calls f()
and blindly copies eax
into a location on the stack (where i
is). Therefore, i == 0
.
Let's try something more complicated with an object and a constructor:
struct S { int i=42; };
S f()
{
if (false)
return {};
// oops
}
int main()
{
S s = f();
}
What main()
does is basically reserve sizeof(S)
bytes on the stack, put the address of the first byte in eax
and then call f()
:
lea eax, _s$[ebp]
push eax
call f
Again, f()
won't do anything, as it will unconditionally jump to the end of the function:
f:
xor eax, eax
je label
; some stuff
; call S::S(), which would set i to 42
; but none of that will happen
label:
ret
So what happened to the sizeof(S)
bytes in main? They were never changed. They contain whatever was already in memory at that particular location. They contain garbage.
This is with an unoptimized build, on a given version of a given compiler. Change the compiler, change the behaviour. Enable the optimizer, drastically change the behaviour.
Don't do it.
In C++, what happens when a function that is supposed to return an object ends without a return statement?
It causes undefined behavior. No one can tell what exactly will happen.