Calling a non-void function without using its return value. What actually happens?
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 o开发者_如何学JAVAther 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.
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.
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.
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
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.
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.
精彩评论