【问题标题】:Where is return value from function stored函数的返回值存储在哪里
【发布时间】:2014-08-06 13:52:01
【问题描述】:

我已经阅读了几篇关于堆栈、堆以及它们在程序执行中的使用方式的文章。 Here 就是其中之一。 总是说在调用函数时,它的参数和局部变量都被放入堆栈(严格来说这是不正确的,正如 Eric Lippert 在his post 中描述的那样,但这不是我现在的问题)。

我的问题是函数的返回值存储在哪里以及它如何传递给调用者?没有人说它放在堆栈上,但仍然没有人说它不是。谁能澄清一下?

例如,考虑以下函数:

public DateTime GetTomorrowDate()
{
    return DateTime.Now.AddDays(1).Date;
}

我的理解是它会为返回值声明一个局部变量吗?如果是这样,为什么当函数返回并且堆栈帧被破坏时它没有被破坏?它是否在调用者的堆栈帧中声明(即使调用者没有使用它)?或者它可能存储在寄存器中的某个位置(我不相信,因为某些自定义结构可能没有合适大小的寄存器)。

【问题讨论】:

  • 这些是实现细节。这个函数最终也可能被内联。
  • @Habib,问题不在于这个特定的功能。这只是一个例子。我的问题是了解 CLR 如何处理它。如果有不同的可能性 - 它们适用于哪些情况?
  • 在幕后,返回值将被存储在一个寄存器中——在这种情况下,作为一个地址指向一个 DateTime 的底层实现。 CLR 将根据对返回对象的引用持有多长时间来管理对象的生命周期。
  • 返回值存储在寄存器中 - 在 x64 上它进入 RAX。在 x86 上,像 DateTime 这样的 64 位结构在 EAX:EDX 中返回。
  • 在示例中,有一个 AddFive 方法。它的返回值到栈尾(因为 Int32 是值类型),和在 C/C++ 中的工作方式一样,没有太大区别。 DateTime 也是如此,因为它也是一种值类型。唯一的区别是它的大小更大,并且在堆栈中分配了更多空间。

标签: c# clr


【解决方案1】:

有几种策略(我用过)。

  1. 返回值小到足以放入寄存器,并从函数返回为为此目的而保留的寄存器中的值。如果需要,这可以扩展到 2 个或更多寄存器。
  2. 返回值在调用者的栈帧上创建为一个临时变量。然后将临时对象的引用(指针)压入堆栈,以便返回值充当额外的 out 参数。在某些语言中,该变量作为命名变量(“结果”)出现在被调用函数中,而在其他语言中,编译器会生成对该参数的移动。
  3. 创建了两个局部变量,一个在函数内部,一个在函数外部。该值在函数退出时从一个复制到另一个。
  4. 返回值只是作为一个额外的参数创建的。在该参数中找到的值由调用者在展开堆栈之前提取。
  5. 该值在“特殊”寄存器中返回,例如浮点累加器。
  6. 该值放置在已知位置(例如任务框架),以后可以从中检索。

可能还有其他人,但这是一个好的开始。


我的回答是基于将问题阅读为想要总结常用技术。在 C# 的上下文中,它适用于 JIT 生成的代码,但不适用于 CIL 本身。

典型的面向堆栈的 VM 语言(包括 CIL)主要通过在函数返回时将它们留在堆栈中来从函数返回值。参数位于其下方,因此在函数返回后需要进行一些堆栈清理。

正如@eric 所说,很难看出这些信息何时有用。显然,从函数返回大值类型可能会对性能产生影响,但这只是预期的结果。

CIL 有很好的文档记录,但 JIT 编译和可能的其他机制没有,这将进一步降低任何此类见解的有用性。

【讨论】:

  • 好答案。这是我期望听到的。我想知道为什么至少在博客中没有记录(或者我根本找不到)。
  • @OleksandrPshenychnyy:为什么要记录下来?您将根据该文档做出什么决定? 根本不需要涉及堆栈帧;当您 return 来自 async 方法的值时会发生什么?关联Task的结果字段被设置;根本不涉及堆栈或寄存器。所有这些都是实现细节,您不必担心。
  • @EricLippert,我的问题只是出于好奇而不是因为我需要根据答案做出一些决定。正如您在博客中所强调的,存储局部变量、类字段等也是实现细节。但这并没有停止撰写数百篇关于此的文章。我想知道为什么每个人都提到方法参数和局部变量,而忽略返回值。我认为一篇文章,即使是很小的一篇,描述可能的选项以及在某些特定版本的 CLR 中使用的选项会非常有趣。不幸的是,我找不到那个。
  • @OleksandrPshenychnyy:你说得对,这将是一篇博客文章的好主题。对于文档来说,这将是一个糟糕的话题:文档应该告诉你可以使用和依赖的关于工具面向公众的表面的事实,而不是有趣的实现细节。
  • @EricLippert,同意它不应该是官方文档。如果存在这样的帖子以扩展我对 CLR 内部的理解,我很乐意阅读。目前 david.pfx 答案是我在这个主题上最好的答案。
猜你喜欢
  • 2018-11-09
  • 2017-09-11
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-28
  • 1970-01-01
相关资源
最近更新 更多