关于是否使用不安全代码和stackalloc固定的困惑

时间:2010-07-26 22:42:00

标签: c# arrays types unsafe

我在下面有一行代码,只有一行注释掉了。 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;
    }
}

3 个答案:

答案 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回答,谁在我面前写了这篇文章)。