为什么stackalloc不能与引用类型一起使用?

Kon*_*ran 8 c# stackalloc

如果stackalloc与参考类型一起使用如下

var arr = stackalloc string[100];
Run Code Online (Sandbox Code Playgroud)

有一个错误

不能获取地址,获取大小,或声明指向托管类型的指针('string')

为什么会这样?为什么CLR不能声明指向托管类型的指针?

Han*_*ant 12

将 C# 编译器生成的 MSIL 转换为可执行机器代码时,.NET 中的即时编译器执行两项重要职责。最明显的就是生成机器代码。不明显且完全不可见的工作是生成一个表,该表告诉垃圾收集器在方法执行期间发生 GC 时在哪里查找对象引用。

这是必要的,因为对象根不仅可以作为类的字段存储在 GC 堆中,还可以存储在局部变量或 CPU 寄存器中。为了正确地完成这项工作,抖动需要知道堆栈帧的确切结构以及存储在那里的变量的类型,以便它可以正确地创建该表。这样,稍后垃圾收集器就可以弄清楚如何读取正确的堆栈帧偏移量或 CPU 寄存器来获取对象根值。指向 GC 堆的指针。

当你使用时这是一个问题stackalloc。该语法利用了 CLR 功能,允许程序声明自定义值类型。围绕普通托管类型声明的后门,限制是该值类型不能包含任何字段。只是一个内存块,由程序来生成该块的正确偏移量。C# 编译器可根据类型声明和索引表达式帮助您生成这些偏移量。

在 C++/CLI 程序中也很常见,相同的自定义值类型功能可以为本机 C++ 对象提供存储。只需要存储该对象的空间,如何正确初始化它并访问该 C++ 对象的成员是 C++ 编译器的工作。GC 无需了解任何内容。

因此,核心限制是无法为该内存块提供类型信息。就 CLR 而言,这些只是没有结构的普通字节,GC 使用的表没有选项来描述其内部结构。

不可避免的是,您可以使用的唯一类型是不需要 GC 需要了解的对象引用的类型。Blittable 值类型或指针。所以System.String是不行的,它是一个引用类型。您可能得到的最接近的“字符串”是:

  char** mem = stackalloc char*[100];
Run Code Online (Sandbox Code Playgroud)

进一步的限制是,完全由您来确保 char* 元素指向固定字符串或非托管字符串。并且您不会将“数组”索引越界。这不太实用。


xan*_*tos 7

"问题"更大:在C#中,您不能拥有指向托管类型的指针.如果你尝试写(在C#中):

string *pstr;
Run Code Online (Sandbox Code Playgroud)

你会得到:

不能获取地址,获取大小,或声明指向托管类型的指针('string')

现在,stackalloc T[num]返回a T*(例如参见此处),因此显然stackalloc不能与引用类型一起使用.

你不能拥有一个指向引用类型的指针的原因可能与GC可以自由地在内存中移动引用类型(以压缩内存)这一事实有关,因此指针的有效性可能很短.

请注意,在C++/CLI中,可以固定引用类型并获取其地址(请参阅pin_ptr)