16

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

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

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

4

5 回答 5

17

我相信对于 C# 和 Java,结果最终都会在堆栈上,然后编译器会强制弹出指令忽略它。Eric Lippert 的博客文章“The void is invariant”对此提供了更多信息。

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

using System;

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

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

.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使用 void 方法,这些调用就会消失。

于 2010-10-22T15:31:39.500 回答
7

编译器是做什么的?

编译器生成一条弹出指令,将结果从虚拟堆栈中丢弃。

运行时环境是做什么的?

它通常将代码转换为将返回值传回寄存器而不是堆栈位置的代码。(通常是 x86 架构上的 EAX。)

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

您关心哪个运行时环境?它们有很多,而且它们都有不同的抖动。

于 2010-10-22T18:42:39.577 回答
3

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

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

于 2010-10-22T15:37:45.733 回答
1

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

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

编辑:更正了一个错字。

于 2010-10-22T16:40:25.123 回答
1

对于 C++,编译器通常会优化变量的返回,将其转换为 void 函数,如果这样可以进一步优化,编译器可能会优化整个函数调用或仅与返回值有关的部分函数调用. 对于 Java 和 C#,我几乎没有什么想法。

于 2010-10-22T16:40:29.490 回答