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

Des*_*ted 3 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 13

我的问题是为什么当行被取消注释时它可以工作,但在注释掉时返回垃圾.

注释行是掩盖 CreateArray引起的错误的原因.评论它暴露了这个bug.但无论如何,这个bug都存在.

正如规范明确指出:

在该函数成员返回时,将自动丢弃在执行函数成员期间创建的所有堆栈分配的内存块.

CreateArray函数分配一个块,存储指向块的指针,块被丢弃,现在你有一个指向垃圾块的指针.您需要永远的指针存储到stackalloc'd块,使得块失效后存储可以被访问.如果您需要存储对它的引用,Heap会分配块,并且在完成后记得取消分配它.

请记住,在不安全的代码中,您需要完全了解托管内存模型的所有内容. 一切.如果您不了解托管内存的所有内容,请不要编写不安全的代码.

那就是说,让我们解决一下你似乎更大的困惑,那就是"什么时候需要修复内存来获取指针?" 答案很简单.当且仅当它是可移动内存时,您必须修复内存. 修复将可移动存储器转换为不可移动的存储器; 这就是修复的目的.

你只能拿一些不可移动的东西; 如果你取一个可移动的东西的地址然后移动那么显然地址是错误的.您需要确保在获取其地址之前内存不会移动,并且您需要确保在再次移动后不再使用该地址.

  • @Eric-干杯.奇怪的是,尽管在一些涉及堆栈*酷刑*的真正*疯狂*`ILGenerator`代码中有很多乱七八糟,`stackalloc`(以及IL对应物)是我必须使用(在生产代码中)精确*永远*. (3认同)
  • @Dested:通过创建一个数组然后将其修复到适当的位置,将它从托管堆中分配出来,或者通过任何你喜欢的非托管分配器将它分配给非托管堆.我相信Marshal类有一堆非托管内存分配器.然后,您需要(1)在完成内存时取消修复或释放内存,并且(2)在unfix/free操作后不保留指针.请记住,*您需要正确访问内存.*通过关闭安全系统,您可以负责内存模型通常负责的所有内容. (2认同)