【问题标题】:Does the value returned by reference in C# 7.0 get stored on the stack or the heap?C# 7.0 中通过引用返回的值是否存储在堆栈或堆中?
【发布时间】:2017-09-06 22:46:34
【问题描述】:

由于引入了 C# 7.0 的按引用返回功能,并且根据我的理解,像这样的功能需要在编译器上进行一些重新布线以仅将此引用存储在位于堆上的变量上,是否可以存储返回对堆栈上变量的引用,还是新的 ref 声明确保变量始终存储在堆上?

ref int x = ref DoSomething(data); 
// Is the value of x now on the stack or the heap? Or is x stored on the stack as a reference and the value on the heap?

我的理解是基于this article

最后,CLR 确实允许“ref 返回类型”;理论上你可以 有一个方法“ref int M() { ... }”返回一个对 整数变量。如果出于某种奇怪的原因我们曾经决定允许 在 C# 中,我们必须修复编译器和验证器,以便它们 确保只能将 refs 返回到 已知在堆上,或已知在堆栈上“较低” 比被调用者。

【问题讨论】:

  • 如果它是一个局部变量,那么它肯定会在堆栈上
  • @EhsanSajjad 如果它只是一个局部变量,那么您无法从 ref 返回方法返回它。
  • 你能详细说明@Servy 的意思吗?
  • @EhsanSajjad 自己试试看。它不会编译,编译器错误会或多或少地告诉你我告诉你的内容。如果您可以从 ref-returning 方法返回对本地的引用,那么您将引用一个超出范围的变量,而 C# 不允许这样做。
  • Eric 引用的内容回答了您的问题。返回的引用必须是对堆中值的引用或堆栈上更高的值。所以它也可能是。

标签: c# clr


【解决方案1】:

这首歌的名字叫“哈多克斯的眼睛”。 “哦,这就是这首歌的名字,是吗?”爱丽丝说,试图引起兴趣。 “不,你不明白,”骑士说,看上去有点恼火。 '名字就是这么叫的。这个名字真的是“老人”。 “那我应该说‘这首歌就是这么叫的’?”爱丽丝纠正了自己。 “不,你不应该:那完全是另一回事!这首歌叫做“方法和手段”:但这只是它的名字,你知道的! “那么,那首歌是什么?”爱丽丝说,此时她已经完全不知所措了。 “我正要讲这个,”骑士说。 '这首歌真的是'A-sitting On a Gate':这首歌是我自己发明的。'

这首歌,这首歌的名字,叫什么名字,叫什么名字,显然都是不同的。


你不能混淆三件事:

  • 对变量的引用的位置 -- 别名
  • 引用变量的位置——别名变量。
  • 如果引用的变量是引用类型,则引用的事物的位置(如果有)。

ref int x = ref DoSomething(data); 

x 的值现在是在栈上还是堆上?

表达式x另一个变量的别名

那个变量有一个值。

假设别名变量是值类型。别名变量及其值是在临时池(也称为“堆栈”)还是长期池(“堆”)上?我们不知道。我们也不在乎。我们知道,无论它是什么,我们都可以保证这个变量现在是有效的。

不过我们可以猜测一下:ref 返回的变量通常是堆分配变量的别名,因为我们确定它们是活动的

假设别名变量是引用类型。 引用是存储在栈上还是堆上?同样,出于同样的原因,我们不知道。 该引用的所指对象是在堆栈还是堆上?它要么在堆上,要么为空;我们保证这一点。

还是x作为引用存储在栈上,值存储在堆上?

x 是指x 别名的变量,还是本地x 本身?本地本身存储在堆栈或寄存器中;它永远不会被提升到闭包类的字段。变量 aliased 可以在任何地方,但如上所述,很可能在堆上。

【讨论】:

  • 我喜欢你的回答。但是 C# 7 如何确保变量在创建别名时是活动的?我目前无法尝试,但不能在同一方法中返回对局部变量(在堆栈上)的引用吗?
  • @IllidanS4:编译器必须跟踪每个 ref 的来源以及发生的情况。将我们知道是本地参考的参考返回是不合法的。
  • 在所有情况下都可以这样做吗?如果在另一个已经编译的程序集中有一个 ref-returning ref-taking 方法,它只返回它提供的引用怎么办?即使在这种情况下,编译器也会检查方法吗?
  • @IllidanS4:好问题。所有的设计问题都是关于解决原则之间的冲突。在这种情况下,原则是“规则足够简单,凡人都能理解”,“规则确保类型安全”,以及“规则不允许很多实际安全但我们不能 -检测它”程序。第二个是必须的,所以必须在第一个和第三个之间做出权衡。
  • @IllidanS4:实际实现的规则是:当且仅当作为参数传递给被调用者的每个 ref 都是安全的时,被调用者返回的 ref 是安全的返回。该规则很容易理解,并且不会使太多实际安全的程序成为非法程序。
【解决方案2】:

ref int xint* x 的语法糖,即变量在运行时的行为方式。当您将变量作为参数传递给方法并且参数声明为ref 时,您会得到完全相同的行为,调用者将指针传递给变量。指针不关心值存储在哪里,实际上可以存储在任何地方。请注意,您不能传递属性,它没有存储空间。

他们在 C# v7 中必须做的重要事情是确保您不会意外创建 dangling pointer bug。在 C 和 C++ 等语言中臭名昭著,在这些语言中它是未定义的行为,编译器不需要生成诊断。许多带有该错误的程序似乎运行得很好,直到程序中看似微不足道的更改(添加函数调用)破坏了指向的值。

实际上并不难做 afaik,被调用方法的局部变量是麻烦制造者。在方法返回后它们不再存在,因此引用变得无效。在 C# v7 中会给你一个编译错误的特定场景。但是如果局部变量是调用者之一,并且它通过参数传入引用,该变量仍然存在,那也没关系。

【讨论】:

    猜你喜欢
    • 2021-01-13
    • 2015-03-13
    • 2013-12-06
    • 2011-07-18
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 2021-01-25
    • 1970-01-01
    相关资源
    最近更新 更多