Nick Proskuryakov Asked:2020-11-29 23:19:45 +0800 CST2020-11-29 23:19:45 +0800 CST 2020-11-29 23:19:45 +0800 CST 堆栈上对象的 C# 地址 772 我的一个朋友问,在C#中,当堆将对象的地址(对对象的引用)存储在堆栈上时,是否可能出现这种情况?鉴于仅使用托管代码。发生争议) c# 1 个回答 Voted Best Answer VladD 2020-11-30T03:22:59+08:002020-11-30T03:22:59+08:00 看。 首先,这个问题在 C# 语言的上下文中没有意义,与它的具体实现隔离开来。就其本身而言,堆栈和堆的存在是 Microsoft CLR 实现的一个细节,无堆栈实现是很有可能的。如果您查看C# 5 规范,您会发现单词 stack 仅出现在不安全的上下文中(例如,它stackalloc返回指针,而不是引用)。C# 语言,除了不安全的部分,并没有准确定义对象和值的存储位置和方式。 因此,仅在 Microsoft CLR 的上下文中考虑该问题是有意义的。对她来说,最简单的方法就是看说明书。 根据规范(§I.12.1.1.2),托管对象地址分为对象引用(O)和托管指针(&)。对象总是在堆上,所以我们只对托管指针感兴趣。 让我们看一下§I.8.2.1.1 托管指针和相关类型: 托管指针类型仅允许用于局部变量(§I.8.6.1.3)和参数签名(§I.8.6.1.4);它们不能用于字段签名(§I.8.6.1.2),因为数组的元素类型(§I.8.9.1),并且不允许对托管指针类型的值进行装箱(§I.8.2.4) . 对方法的返回类型(§I.8.6.1.5)使用托管指针类型是不可验证的(§I.8.8)。 [理由:出于性能原因,GC堆上的项目可能不包含对其他GC对象内部的引用,这激发了对字段和装箱的限制。进一步返回引用局部变量或参数变量的托管指针可能会导致引用比变量寿命更长,因此它是不可验证的。结束理由] 俄语相同(我的翻译): 托管指针类型仅允许用于局部变量 (§I.8.6.1.3) 和形式参数 (§I.8.6.1.4);它不能用作字段类型 (§I.8.6.1.2) 或数组元素类型 (§I.8.9.1)。此外,不允许托管指针值装箱(§I.8.2.4)。使用托管指针作为方法的返回类型(§I.8.6.1.5)会导致无法验证的代码(§I.8.8)。 [动机:垃圾收集器服务的对象,出于性能原因,不应包含指向其他收集对象中间的指针,这迫使我们对边距和包装设置限制。此外,将托管指针返回到局部变量或参数可能会导致指针的生命周期超过变量的生命周期,从而导致无法验证。动机结束] 因此,我们看到从堆到堆栈的(托管)指针是被禁止的:托管指针不能在堆上结束。 并且从堆到堆栈的非托管指针是可能的,例如: unsafe class U { char* p; unsafe U() { char* q = stackalloc char[1]; p = q; } } PS:C# 7.2引入了 typeSpan<T>,它可以包含不安全上下文之外的堆栈引用。但是你不能放在Span<T>一堆。
看。
首先,这个问题在 C# 语言的上下文中没有意义,与它的具体实现隔离开来。就其本身而言,堆栈和堆的存在是 Microsoft CLR 实现的一个细节,无堆栈实现是很有可能的。如果您查看C# 5 规范,您会发现单词 stack 仅出现在不安全的上下文中(例如,它
stackalloc
返回指针,而不是引用)。C# 语言,除了不安全的部分,并没有准确定义对象和值的存储位置和方式。因此,仅在 Microsoft CLR 的上下文中考虑该问题是有意义的。对她来说,最简单的方法就是看说明书。
根据规范(§I.12.1.1.2),托管对象地址分为对象引用(
O
)和托管指针(&
)。对象总是在堆上,所以我们只对托管指针感兴趣。让我们看一下§I.8.2.1.1 托管指针和相关类型:
俄语相同(我的翻译):
因此,我们看到从堆到堆栈的(托管)指针是被禁止的:托管指针不能在堆上结束。
并且从堆到堆栈的非托管指针是可能的,例如:
PS:C# 7.2引入了 type
Span<T>
,它可以包含不安全上下文之外的堆栈引用。但是你不能放在Span<T>
一堆。