简体   繁体   English

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

[英]Confusion on whether to use fixed with unsafe code and stackalloc

I have a block of code below with a single line commented out. 我在下面有一段代码,只有一行注释掉了。 What happens in the CreateArray method is the same thing that the commented out line does. CreateArray方法中发生的事情与注释掉的行相同。 My question is why does it work when the line b->ArrayItems = d is uncommented, but return garbage when commented out? 我的问题是为什么当b->ArrayItems = d被取消注释时它会起作用,但是当注释掉时返回垃圾? I don't think I have to "fixed" anything, because all of the information is unmanaged. 我不认为我必须“修复”任何东西,因为所有信息都是不受管理的。 Is this assumption incorrect? 这个假设是不正确的?

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;
    }
}

My question is why does it work when the line is uncommented, but return garbage when commented out. 我的问题是为什么当行被取消注释时它会起作用,但在注释掉时返回垃圾。

The commented line is what is masking the bug caused by CreateArray. 注释行是掩盖 CreateArray引起的错误的原因。 Commenting it out exposes the bug. 评论它暴露了这个bug。 But the bug is there regardless. 但无论如何,这个bug都存在。

As the specification clearly states: 正如规范明确指出:

All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. 在该函数成员返回时,将自动丢弃在执行函数成员期间创建的所有堆栈分配的内存块。

The CreateArray function allocates a block, you store a pointer to the block, the block is discarded, and now you have a pointer to a garbage block. CreateArray函数分配一个块,存储指向块的指针,块被丢弃,现在你有一个指向垃圾块的指针。 You are required to never store a pointer to a stackalloc'd block such that the storage can be accessed after the block becomes invalid. 必须 永远不会存储指向stackalloc'd块的指针,以便在块变为无效后可以访问存储。 Heap allocate the block if you need to store a reference to it, and remember to deallocate it when you're done. 如果您需要存储对它的引用,Heap会分配块,并且在完成后记得取消分配它。

Remember, in unsafe code you are required to fully understand everything about the managed memory model. 请记住,在不安全的代码中, 您需要完全了解托管内存模型的所有内容。 Everything . 一切 If you don't understand everything about managed memory, don't write unsafe code. 如果您不了解托管内存的所有内容,请不要编写不安全的代码。

That said, let's address what seems to be your larger confusion, which is "when do you have to fix memory to obtain a pointer?" 那就是说,让我们解决一下你似乎更大的困惑,那就是“什么时候需要修复内存来获取指针?” The answer is simple. 答案很简单。 You have to fix memory if and only if it is movable memory. 当且仅当它是可移动内存时,您必须修复内存。 Fixing transforms movable memory into immovable memory; 修复将可移动存储器转换为不可移动的存储器; that's what fixing is for . 这就是修复的目的

You can only take the address of something that is immovable; 你只能拿一些不可移动的东西; if you take the address of something that is movable and it moves then obviously the address is wrong. 如果你取一个可移动的东西的地址然后移动那么显然地址是错误的。 You are required to ensure that memory will not move before you take its address, and you are required to ensure that you do not use the address after it becomes movable again. 您需要确保在获取其地址之前内存不会移动,并且您需要确保在再次移动后不再使用该地址。

Stackalloc allocates some space on the callstack, that space is then lost when you move up out of the current level of context (for example, leaving a method). Stackalloc在callstack上分配一些空间,当你向上移出当前的上下文级别时(例如,留下一个方法),该空间就会丢失。 You're problem is that when the stackalloc is inside a method then that area of the stack is no longer yours to play with when you leave that method. 你的问题是当stackalloc在一个方法中时,当你离开那个方法时,堆栈的那个区域就不再是你的了。

So, if you do this: 所以,如果你这样做:

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

"stuff" is only valid inside foo, once you leave foo the stack is wound back, which means that if you do this: “stuff”只在foo中有效,一旦你离开foo,堆栈就会被收回,这意味着如果你这样做:

foo()
{
    byte* allocate()
    {
        return stackalloc[1]
    }

    stuff = allocate()
    do something with stuff
}

then the return value of allocate becomes rubbish when you leave the allocate method, which means that "stuff" never makes any sense. 当你离开allocate方法时,allocate的返回值变成垃圾,这意味着“东西”从来没有任何意义。

Your assumption is partially correct, but understood incorrectly. 您的假设部分正确,但错误理解。 Here's a quote from this MSDN page : 以下是此MSDN页面的引用:

In unsafe mode, you can allocate memory on the stack, where it is not subject to garbage collection and therefore does not need to be pinned. 在不安全模式下,您可以在堆栈上分配内存,它不受垃圾回收的影响,因此不需要固定。 See stackalloc for more information. 有关更多信息,请参阅stackalloc

Some statements will allocate variables on the stack automatically (ie, value types inside a method), others will need to be specified specifically using stackalloc . 有些语句会自动在堆栈上分配变量(即方法中的值类型),其他stackalloc需要使用stackalloc专门指定。

Stack allocated memory is discarded after the method ends, hence your issue (see Eric Lipperts answer, who wrote this before me). 在方法结束后丢弃堆栈分配的内存,因此您的问题(请参阅Eric Lipperts回答,谁在我之前写了这个)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM