.net C#指针和值类型变量的重定位

2skhul33  于 2023-08-08  发布在  .NET
关注(0)|答案(1)|浏览(129)

我已经写了下面的unsafe类,它将指针封装到int:

unsafe class PtrWrapper
{
   public PtrWrapper(ref int val)
   {
       fixed (int* ptr = &val)
       {
           m_ptr = ptr;
       }
   }
   public int Value
   {
       get { return *m_ptr; }
       set { *m_ptr = value; }
   }
   int * m_ptr;
}

字符串
我已经测试过了,它似乎工作得很好,但是我刚刚再次读取了reference,看起来指针上的所有操作都应该在语句中完成:
如果没有fixed,指向可移动的托管变量的指针将没有什么用处,因为垃圾收集可能会不可预测地重新定位变量。
那么,有没有可能在我调用Value属性时,指针在内存中被重新定位,而我的指针指向其他东西?我知道如果指针对象超出了作用域,就会出现问题,但我将通过如何使用类来说明这一点。所以我问的是关于一个值类型变量的 * 重新定位 *,这个变量还没有 * 超出作用域。

mefy6pfw

mefy6pfw1#

是的,很有可能,甚至很有可能。
在堆上:

class M 
{
    public int i;
    public PtrWrapper w;
    
    public M()
    {
        i = 42;
        w = new PtrWrapper(ref i);
    }
}
    
var m = new M();
var value = m.w.Value; // probably 42

// move m to gen 2
for (int i = 0; i < 10; i++)
{
    GC.Collect();
}

Debug.Assert(GC.GetGeneration(m) == 2);

value = m.w.Value; // probably a random value or AccessViolationException

字符串
这就是为什么它被称为“不安全”。fixed只会在范围内阻止某些东西移动。
但是在堆栈上应该没问题。只有当你超出作用域时,堆栈上的变量才会被弹出,比如在这种情况下:

PtrWrapper M()
{
    var i = 42;
    var w = new PtrWrapper(ref i);
    return w;
}

var w = M();
Console.WriteLine(); // do something else on the stack
var value = w.Value; // some random value


注意,捕获的变量(在lambdas中)实际上在堆上,因此在这种情况下,您应该小心使用这些变量。

相关问题