views:

295

answers:

5

So, I found a similar question here, but the answers are more about style and whether or not you are able to do it.

My question is, what actually happens when you call a non-void function that returns an object, but you never assign or use said returned object? So, less about whether or not you can, because I absolutely know you can and understand the other question linked above... what does the compiler/runtime environment do?

This is not a language specific question, but if you answer, please specify what language you are referring to, since behaviors will differ.

+12  A: 

I believe that for both C# and Java, the result ends up on the stack, and the compiler then forces a pop instruction to ignore it. Eric Lippert's blog post on "The void is invariant" has some more information on this.

For example, consider the following C# code:

using System;

public class Test 
{
    static int Foo() { return 10; }

    public static void Main()
    {
        Foo();
        Foo();
    }
}

The IL generated (by the MS C# 4 compiler) for the Main method is:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    L_0000: call int32 Test::Foo()
    L_0005: pop 
    L_0006: call int32 Test::Foo()
    L_000b: pop 
    L_000c: ret 
}

Note the calls to pop - which disappear if you make Foo a void method.

Jon Skeet
Maybe C# isn't that smart, but in this example, wouldn't the compiler inline the method? Also, how expensive is popping the stack for such a call? You might see a difference if this were looped 250k times, but for your average method call I wouldn't lose sleep over an extra pop or two, unless you never used the return value anywhere in code.
KeithS
@Jon Great answer thank you!
DomenicDatti
@KeithS I was wondering how expensive it is too, but in my particular case it's not a concern. I have a method that does some caching behind the scenes into multiple maps but also returns the requested list, and another method that relies on the caching and returns a different list from one of the cached maps that the first method generates. I supposed I could refactor the map generation to make it cleaner and avoid this extra pop altogether if necessary.
DomenicDatti
@Keith: The C# compiler wouldn't inline it, but the JIT might well do so. Ditto in Java. Most optimizations in managed languages are done by the VM rather than the language compiler.
Jon Skeet
+2  A: 

It depends a bit on the calling convention being used. For small/simple types, the return will typically happen in a register. In this case, the function will write the value into the register, but nothing else will pay attention, and the next time that register is needed (which will typically happen pretty quickly) it'll be overwritten with something else.

For larger types, the compiler will normally allocate a structure to hold the return value. Exactly where/how it does that allocation will vary with the compiler though -- in some cases it'll be a static structure, and the contents will be ignored and overwritten the next time you call a function returning the same type. In other cases it'll be on the stack, and even though you haven't used it, it still needs to be allocated before the function is called, and freed afterwards

Jerry Coffin
@Jerry What language are you referring to?
DomenicDatti
@DomenicDatti: I was trying to keep the answer general enough to apply reasonably well to all three. On a virtual machine, the stack will be virtualized, and it can't use registers until the JIT compiler has run, but both Java and .NET normally use a JIT. Neither of those has any major effect on the result though.
Jerry Coffin
+1  A: 

In .NET, if the object being returned is a reference type, and the application has no other references to that object, then you'll still have an object floating around in memory until the garbage collector decides to collect it.

This is potentially bad if the object being returned happens to be holding on to resources. If it implements the IDisposable interface, then your code ought to be calling the Dispose method on it, but in this case the Dispose method would never be called.

EDIT: corrected a typo.

Dr. Wily's Apprentice
+1  A: 

For C++, typically, the compiler will optimize out the returning of the variable, turning it into a void function, and if this enables further optimizations, the compiler may optimize out the entire function call or parts of it that only pertain to the return value. For Java and C#, I have little idea.

DeadMG
+4  A: 

what does the compiler do?

The compiler generates a pop instruction that discards the result off the virtual stack.

what does the runtime environment do?

It typically jits the code into code that passes the return value back in a register, rather than a stack location. (Typically EAX on x86 architectures.)

The jitter knows that the value will go unused, so it probably generates code that clears the register. Or perhaps it just leaves it hanging around in the register for a while.

Which runtime environment do you care about? There are lots of them, and they all have different jitters.

Eric Lippert