代码之家  ›  专栏  ›  技术社区  ›  T. Stone

关于是否将fixed与不安全代码和stackalloc一起使用的混淆

  •  3
  • T. Stone  · 技术社区  · 14 年前

    下面有一段代码,只有一行被注释掉了。发生了什么 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 回复  |  直到 14 年前
        1
  •  13
  •   Eric Lippert    14 年前

    我的问题是,当行未注释时,它为什么会工作,但在注释掉时返回垃圾。

    注释行是什么 掩蔽 由CreateArray引起的错误。把它注释掉会暴露这个错误。但不管怎样,虫子还是存在的。

    规范明确规定:

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

    createArray函数分配一个块,您存储一个指向该块的指针,该块被丢弃,现在您有一个指向垃圾块的指针。你是 必修的 从不存储指向stackalloc'd块的指针 以便在块无效后可以访问存储。如果需要存储对块的引用,则堆分配该块,完成后请记住取消分配。

    记住,在不安全的代码中 你必须完全理解 一切 关于托管内存模型。 一切 . 如果您不了解有关托管内存的所有信息,请不要编写不安全的代码。

    这就是说,让我们来讨论一下您更大的困惑,即“什么时候必须修复内存才能获得指针?”答案很简单。 如果并且仅当它是可移动存储器时,您必须修复存储器。 固定将可移动记忆转化为不可移动记忆; 这就是修复的目的 .

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

        2
  •  1
  •   Martin    14 年前

    stackalloc在调用堆栈上分配一些空间,当您向上移出当前上下文级别(例如,离开一个方法)时,该空间就会丢失。您的问题是,当stackalloc在一个方法中时,当您离开该方法时,该堆栈的区域就不再是您可以使用的了。

    所以,如果你这样做:

    foo()
    {
        stuff = stackalloc byte[1]
        Do something with stuff
    }
    

    “东西”只在foo中有效,一旦你离开foo,堆栈就被卷回,这意味着如果你这样做:

    foo()
    {
        byte* allocate()
        {
            return stackalloc[1]
        }
    
        stuff = allocate()
        do something with stuff
    }
    

    然后当您离开allocate方法时,allocate的返回值就变成垃圾,这意味着“stuff”没有任何意义。

        3
  •  1
  •   Abel    14 年前

    你的假设部分正确,但理解错误。下面是一个引文 from this MSDN page :

    在不安全模式下,您可以分配 堆栈上的内存,而不是 接受垃圾收集和 因此不需要固定。 见 stackalloc 更多信息。

    有些语句将自动在堆栈上分配变量(即方法内的值类型),其他语句将需要使用 stackalloc .

    堆栈分配的内存在方法结束后被丢弃,因此是您的问题(请参阅EricLippers的回答,谁在我之前写了这个)。