如果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* 元素指向固定字符串或非托管字符串。并且您不会将“数组”索引越界。这不太实用。
"问题"更大:在C#中,您不能拥有指向托管类型的指针.如果你尝试写(在C#中):
string *pstr;
Run Code Online (Sandbox Code Playgroud)
你会得到:
不能获取地址,获取大小,或声明指向托管类型的指针('string')
现在,stackalloc T[num]返回a T*(例如参见此处),因此显然stackalloc不能与引用类型一起使用.
你不能拥有一个指向引用类型的指针的原因可能与GC可以自由地在内存中移动引用类型(以压缩内存)这一事实有关,因此指针的有效性可能很短.
请注意,在C++/CLI中,可以固定引用类型并获取其地址(请参阅pin_ptr)
| 归档时间: |
|
| 查看次数: |
1258 次 |
| 最近记录: |