我在下面有一行代码,只有一行注释掉了。 CreateArray
方法中发生的情况与注释掉的行相同。我的问题是为什么当行b->ArrayItems = d
被取消注释时它会起作用,但是当注释掉时会返回垃圾?我不认为我必须“修复”任何东西,因为所有信息都是不受管理的。这个假设是不正确的吗?
class Program
{
unsafe static void Main(string[] args)
{
someInstance* b = stackalloc someInstance[1];
someInstance* d = stackalloc someInstance[8];
b->CreateArray();
// b->ArrayItems = d;
*(b->ArrayItems)++ = new someInstance() { IntConstant = 5 };
*(b->ArrayItems)++ = new someInstance() { IntConstant = 6 };
Console.WriteLine((b)->ArrayItems->IntConstant);
Console.WriteLine(((b)->ArrayItems - 1)->IntConstant);
Console.WriteLine(((b)->ArrayItems - 2)->IntConstant);
Console.Read();
}
}
public unsafe struct someInstance
{
public someInstance* ArrayItems;
public int IntConstant;
public void CreateArray()
{
someInstance* d = stackalloc someInstance[8];
ArrayItems = d;
}
}
答案 0 :(得分:13)
我的问题是为什么在取消注释该行时它会起作用,但在注释掉时会返回垃圾。
注释行是屏蔽由CreateArray引起的错误。评论它暴露了这个bug。但无论如何都存在错误。
正如规范明确指出:
当函数成员返回时,将自动丢弃在执行函数成员期间创建的所有堆栈分配的内存块。
CreateArray函数分配一个块,存储指向块的指针,块被丢弃,现在你有一个指向垃圾块的指针。您必需 永远不会存储指向stackalloc'd块的指针,以便在块变为无效后可以访问存储。如果您需要存储对它的引用,Heap会分配块,并且在完成后记得取消分配它。
请注意,在不安全的代码中,您需要完全了解有关托管内存模型的一切。 一切。如果您不了解托管内存的所有内容,请不要编写不安全的代码。
那就是说,让我们解决一下你似乎更大的困惑,即“你什么时候需要修复内存来获取指针?”答案很简单。 当且仅当它是可移动内存时,你必须修复内存。修复将可移动内存转换为不可移动内存; 这就是的修复方式。
你只能拿一些不可移动的地址;如果你取一个可移动的东西的地址然后移动那么显然地址是错误的。您需要确保在获取其地址之前内存不会移动,并且您需要确保在再次移动后不再使用该地址。
答案 1 :(得分:1)
Stackalloc在callstack上分配一些空间,当你向上移出当前的上下文级别时(例如,留下一个方法),该空间就会丢失。你的问题是当stackalloc在一个方法中时,当你离开那个方法时,堆栈的那个区域就不再是你的了。
所以,如果你这样做:
foo()
{
stuff = stackalloc byte[1]
Do something with stuff
}
“stuff”只在foo中有效,一旦你离开foo,堆栈就会被收回,这意味着如果你这样做:
foo()
{
byte* allocate()
{
return stackalloc[1]
}
stuff = allocate()
do something with stuff
}
然后当你离开allocate方法时,allocate的返回值变成垃圾,这意味着“东西”从来没有任何意义。
答案 2 :(得分:1)
您的假设部分正确,但理解不正确。这是引用from this MSDN page:
在不安全模式下,您可以分配 堆栈上的内存,它不是 受垃圾收集和 因此不需要固定。 有关详细信息,请参阅stackalloc。
某些语句会自动在堆栈上分配变量(即方法中的值类型),其他语句需要使用stackalloc
专门指定。
堆栈分配的内存在方法结束后被丢弃,因此你的问题(参见Eric Lipperts回答,谁在我面前写了这篇文章)。