首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么stackalloc不能与引用类型一起使用?

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

Stack Overflow用户
提问于 2016-02-24 09:25:10
回答 4查看 4.7K关注 0票数 11

如果stackalloc与引用类型一起使用,如下所示

代码语言:javascript
复制
var arr = stackalloc string[100];

有一个错误

无法获取托管类型的地址、大小或声明指向托管类型(“字符串”)的指针

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-02-24 09:29:47

“问题”更大:在C#中,不能有指向托管类型的指针。如果您尝试编写(用C#):

代码语言:javascript
复制
string *pstr;

你会得到:

无法获取托管类型的地址、大小或声明指向托管类型(“字符串”)的指针

现在,stackalloc T[num]返回一个T* (参见这里),因此显然stackalloc不能与引用类型一起使用。

不能有指向引用类型的指针的原因可能是GC可以自由地在内存中移动引用类型(以压缩内存),因此指针的有效性可能很短。

请注意,在C++/CLI中,可以将引用类型引脚并获取其地址(参见ptr)。

票数 8
EN

Stack Overflow用户

发布于 2016-02-24 10:37:46

.NET中的实时编译器在将C#编译器生成的C#转换为可执行机器代码时执行两项重要任务。显而易见的,可见的是生成机器代码。不明显和完全不可见的任务是生成一个表,该表告诉垃圾收集器在执行方法时查找对象引用的位置。

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

当您使用stackalloc时,这是一个问题。该语法利用了允许程序声明自定义值类型的CLR功能。普通托管类型声明的后门,但限制此值类型不能包含任何字段。仅仅是一小块内存,这取决于程序生成适当的偏移到该blob。C#编译器帮助您根据类型声明和索引表达式生成这些偏移量。

在C++/CLI程序中也很常见,相同的自定义值类型特性可以为本机C++对象提供存储。只需要存储该对象的空间,如何正确初始化该对象并访问该C++对象的成员是C++编译器确定的任务。没什么GC需要知道的。

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

不可避免的是,您可以使用的唯一类型是不需要GC需要知道的对象引用的类型。闪烁值类型或指针。所以System.String是不允许的,它是一个引用类型.你能得到的最接近的是“严格”:

代码语言:javascript
复制
  char** mem = stackalloc char*[100];

有了进一步的限制,即完全由您来确保char*元素指向固定或非托管字符串。而且你不会索引“数组”超出界限。这不是很实际。

票数 10
EN

Stack Overflow用户

发布于 2016-02-24 09:31:26

因为C#致力于内存安全的垃圾收集,而不是C++,所以您应该了解内存管理的中性值。

例如,看看下面的代码:

代码语言:javascript
复制
public static void doAsync(){
    var arr = stackalloc string[100];
    arr[0] = "hi";
     System.Threading.ThreadPool.QueueUserWorkItem(()=>{
           Thread.Sleep(10000);
           Console.Write(arr[0]);
     });
}

这个程序很容易崩溃。因为arr是堆栈分配的,所以一旦doAsync结束,对象+它的内存就会消失。lamda函数仍然指向这个无效的内存地址,这是无效的状态。

如果通过引用传递本地原语,也会出现同样的问题。

方案是:

静态对象->存在于整个应用程序定位时间。

只要创建本地对象的作用域是有效的,本地对象->就会存在。

堆分配对象(用new创建)只要有人持有对它们的引用,->就存在。

另一个问题是垃圾收集在时间段内工作。当一个对象是本地的,它应该在函数结束后就完成,因为之后--内存将被其他变量覆盖。GC不能强制完成对象,或者不应该,无论如何。

然而,好的是,C# JIT有时(并不总是)能够确定一个对象是否可以安全地分配到堆栈上,并且如果可能的话也会诉诸堆栈分配(同样,有时)。

另一方面,在C++中,您可以在任何地方声明所有内容,但是这比C#或Java更不安全,但是您可以微调应用程序,实现高性能低资源应用程序。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35598078

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档