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

本文关键字:新定位 定位 指针 类型变量 | 更新日期: 2023-09-27 17:58:19

我已经编写了以下不安全的类,它封装了指向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;
}

我已经测试过了,它似乎工作得很好,但我刚刚再次阅读了fixed上的引用,看起来指针上的所有操作都应该在语句中完成:

如果没有固定,指向可移动托管变量的指针将为没有什么用处,因为垃圾回收可以重新定位变量不可预测。

那么,当我调用Value属性时,指针对象是否可能在内存中重新定位,而我的指针指向其他对象?我知道,如果指针对象超出范围,我会遇到问题,但我会通过如何使用我的类来解释。所以我问的是,重新定位一个值类型变量,该变量的尚未超出范围。

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

是的,这很有可能,甚至很有可能。

堆上:

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中)确实在堆中,因此在该场景中应该小心处理这些变量。