C#中引用类型的不安全指针实际指向何处?
C#引用类型不安全指针的指向问题
我们先结合两段代码的表现,明确引用类型不安全指针的实际指向:
代码示例1:赋值null后触发空引用异常
static unsafe void Main(string[] args) { var student = new Student { Name = "Dan", Age = 34 }; Student* myStdPtr = &student; student = null; Console.WriteLine(myStdPtr->Name); } class Student { public string Name; public int Age; }
运行这段代码时,访问myStdPtr->Name会抛出NullReferenceException。本质原因是:Student*指针指向的是栈上存储student引用的内存地址。当把student赋值为null时,栈上的这个引用被覆盖成了空指针,后续通过myStdPtr解引用时,相当于间接访问了一个空引用,自然触发异常。
代码示例2:返回栈指针后仍能运行(未定义行为)
static unsafe void Main(string[] args) { Student* student = getStudent(); Console.WriteLine(student->Name); } static unsafe Student* getStudent() { var student = new Student { Name = "Dan", Age = 34 }; Student* studentPtr = &student; return studentPtr; } class Student { public string Name; public int Age; }
这段代码能正常运行是一种未定义行为,并非指针指向了堆对象。getStudent方法返回的指针指向的是该方法栈帧里的student引用(栈内存),方法返回后栈帧被回收,这块内存已经不属于当前有效范围。只是运行时栈内存还没被新数据覆盖,所以临时能读出正确值——这是偶然情况,一旦后续有操作覆盖这块内存,访问就会出错。
核心结论
在C#中,为引用类型创建unsafe指针时,指针始终指向的是存储该引用的内存位置(通常是栈上的局部变量,或是堆上的某个字段),而不是直接指向堆上的对象实例本身。
引用类型的实例本身位于堆上,而引用(底层是类似指针的结构)存储在栈或堆的字段中。当你用&取引用类型变量的地址时,得到的是这个引用的存储地址,而非实例的地址。如果要直接获取堆上实例的地址,需要借助__makeref、PtrToStructure这类特殊API;只有值类型的指针才会直接指向实例本身(因为值类型实例直接存储在栈或字段所在的内存中)。
内容的提问来源于stack exchange,提问作者Marcin
相关产品推荐
相关产品推荐

