views:

917

answers:

6

Yesterday, I found myself writing code like this:

SomeStruct getSomeStruct()
{
    SomeStruct input;

    cin >> input.x;
    cin >> input.y;
}

Of course forgetting to actually return the struct I just created. Oddly enough, the values in the struct that was returned by this function got initialized to zero (when compiled using g++ that is). Is this just a coincidence or did another SomeStruct get created and initialized somewhere implicitly?

+2  A: 

For me the compiler didn't allow it: http://codepad.org/KkzVCesh

Alex Gaynor
It looks like it didn't allow it because you have your compiler set up to have warnings treated as errors. Do you think this could be the case of me having a warning level set too low somewhere?
Jason Baker
+14  A: 

Falling off the end of a function that is declared to return a value (without explicitly returning a value) leads to undefined consequences. For gcc, you should start with the -Wall command line switch that turns on most useful warnings. The specific gcc warning that controls the warning you want is -Wreturn-type (which is included in -Wall, I just mention this for completeness).

Once you have warnings turned on, you should also use -Werror to treat warnings as errors and make the build stop at the point it detects an error.

Greg Hewgill
I've always been amazed that this is merely a warning instead of an error. It hurts far more often than it helps.
Jim Buck
It was legal to not return anything in C. It was still undefined, but at least it was legal.
Jonathan
+4  A: 

The calling conventions for most modern CPU architectures specify a particular register to pass a function return value back to the caller. The caller makes the function call, and then uses the specified register as the return value. If you do not explicitly return a value, the caller will nonetheless use whatever garbage happens to be in that register.

The compiler will also use all registers it has available for internal computation within the function. The register designated for holding the return value will also be used for misc computation within the function. Thus when you forget to specify a return value it is not unusual to find the correct value has miraculously been returned to the caller: the compiler used that register to store your object.

Unfortunately even a trivial change to the function can cause the register allocation to change, so the return value becomes true garbage.

DGentry
+4  A: 

Did another SomeStruct get created and initialized somewhere implicitly?

Think about how the struct is returned. If both x and y are 32 bits, it is too big to fit in a register on a 32-bit architecture, and the same applies to 64-bit values on a 64-bit architecture (@Denton Gentry's answer mentions how simpler values are returned), so it has to be allocated somewhere. It would be wasteful to use the heap for this, so it has to be allocated on the stack. But it cannot be on the stack frame of your getSomeStruct function, since that is not valid anymore after the function returns.

The compiler instead has the caller tells the called function where to put the result (which is probably somewhere on the stack of the caller), by passing the called function a hidden pointer to the space allocated to it. So, the place where it is being set to zero is on the caller, not on your getSomeStruct function.

There are also optimizations like the "named value return optimization" where extra copies can be elided. So, had you used the missing return, the result would be created directly on the space allocated by the caller, instead of creating a temporary and copying it.

To know more about what is happening, you have to look at the caller function. Is it initializing (to zero) an "empty" SomeStruct to which you later assign the return value of your getSomeStruct function? Or is it doing something else?

CesarB
+1  A: 

You didn't get any warning because you didn't have -Wall -Werror turned on. (As stated in other answers)

However I think you probably got a zero-filled struct as the result because the stack object was default constructed in the caller function, possibly with explicit zero arguments, or else due to zeros on the stack?

Douglas Leeder
+2  A: 

I find this interesting. With default options used, the following compilers have the following behavior when compiling the GetSomeStruct() function:

  • Microsoft VC, all versions (since VC6 anyway):

    error C4716: 'getSomeStruct' : must return a value

  • Digital Mars:

    Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • Comeau:

    warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    no error or warning

Given the following couple of sentences from the standard (6.6.3 Paragraph 2):

A return statement without an expression can be used only in functions that do not return a value, that is, a function with the return type void, a constructor (12.1), or a destructor (12.4). ... 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.

I would say there's little reason for a compiler not to give an error in this case. Why do so many compilers give only a warning or no diagnostic at all?

Michael Burr