3

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

3 回答 3

13

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

注释行是掩盖由 CreateArray 引起的错误的原因。将其注释掉会暴露该错误。但无论如何,错误都在那里。

正如规范中明确指出的那样:

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

CreateArray 函数分配一个块,您存储一个指向该块的指针,该块被丢弃,现在您有一个指向垃圾块的指针。您必须永远不要存储指向 stackalloc'd 块的指针,以便在块变得无效后可以访问存储。如果您需要存储对它的引用,则堆分配块,并记住在完成后释放它。

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

也就是说,让我们解决似乎更大的困惑,即“什么时候必须修复内存才能获得指针?” 答案很简单。当且仅当它是可移动内存时,您必须修复内存。 固定将可移动的记忆转变为不可移动的记忆;这就是修复的目的

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

于 2010-07-26T22:48:40.543 回答
1

Stackalloc 在调用堆栈上分配一些空间,当您向上移出当前上下文级别(例如,离开一个方法)时,该空间会丢失。您的问题是,当 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 的返回值就变成了垃圾,这意味着“东西”永远没有任何意义。

于 2010-07-26T22:49:55.263 回答
1

您的假设部分正确,但理解不正确。这是此 MSDN 页面的引述:

在不安全模式下,您可以在堆栈上分配内存,它不受垃圾回收的影响,因此不需要固定。有关详细信息,请参阅stackalloc

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

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

于 2010-07-26T22:54:53.160 回答