在不使用其返回值的情况下调用非void函数。究竟发生了什么?

时间:2010-10-22 15:29:08

标签: c# java c++ return-value void

所以,我发现了一个类似的问题here,但答案更多的是关于风格以及你是否能够做到。

我的问题是,当你调用一个返回一个对象的非void函数时,实际发生了什么,但你从未分配或使用所述返回的对象?所以,更少关于你是否可以,因为我完全知道你可以理解上面链接的另一个问题......编译器/运行时环境是做什么的?

这不是特定于语言的问题,但如果您回答,请说明您所指的语言,因为行为会有所不同。

5 个答案:

答案 0 :(得分:16)

我相信对于C#和Java,结果都会在堆栈上结束,然后编译器强制使用pop指令来忽略它。 Eric Lippert关于"The void is invariant"的博客文章提供了更多相关信息。

例如,请考虑以下C#代码:

using System;

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

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

Main方法生成的IL(由MS C#4编译器生成)为:

.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 
}

请注意对pop的调用 - 如果您使Foo成为无效方法,则会消失。

答案 1 :(得分:7)

  

编译器做了什么?

编译器生成一条pop指令,用于丢弃虚拟堆栈的结果。

  

运行时环境是做什么的?

它通常将代码嵌入到代码中,该代码将返回值传递回寄存器,而不是堆栈位置。 (通常是x86体系结构上的EAX。)

抖动知道该值将不被使用,因此它可能会生成清除寄存器的代码。或许它只是让它在寄存器中闲置一段时间。

您关心哪个运行时环境?他们有很多,他们都有不同的紧张情绪。

答案 2 :(得分:3)

它取决于所使用的调用约定。对于小型/简单类型,返回通常发生在寄存器中。在这种情况下,函数会将值写入寄存器,但没有其他任何内容会引起注意,下次需要该寄存器时(通常会很快发生)它将被其他东西覆盖。

对于较大的类型,编译器通常会分配一个结构来保存返回值。究竟在哪里/如何进行分配会因编译器而异 - 在某些情况下它将是一个静态结构,下次调用返回相同类型的函数时,内容将被忽略并被覆盖。在其他情况下,它将在堆栈上,即使你没有使用它,它仍然需要在调用函数之前分配,然后释放

答案 3 :(得分:1)

在.NET中,如果返回的对象是引用类型,并且应用程序没有对该对象的其他引用,那么在垃圾收集器决定收集它之前,您仍然会在内存中浮动一个对象。 / p>

如果返回的对象碰巧持有资源,这可能会很糟糕。如果它实现了IDisposable接口,那么你的代码应该在它上面调用Dispose方法,但在这种情况下,永远不会调用Dispose方法。

编辑:纠正错字。

答案 4 :(得分:1)

对于C ++,通常,编译器会优化变量的返回,将其转换为void函数,如果这样可以进一步优化,编译器可以优化整个函数调用或其中仅属于返回值。对于Java和C#,我不知道。