在C#中获取托管对象内存地址需用fixed语句固定数组或字符串、GCHandle.Alloc固定引用类型对象、或Unsafe.AsPointer获取ref变量地址,均需不安全上下文且注意GC影响。

在C#中,托管对象默认由垃圾回收器管理,其内存地址可能随时变动;若需直接操作其内存地址,必须使用fixed语句固定对象位置,防止GC移动。以下是获取托管对象内存地址的具体方法:
一、使用fixed语句固定数组并获取首元素地址
fixed语句可将托管数组的内存位置“钉住”,使其在作用域内不被垃圾回收器移动,并允许获取指向首元素的指针。该方法仅适用于一维数组(包括string),且必须在不安全上下文中使用。
1、在项目属性中启用“允许不安全代码”选项。
2、在代码文件顶部添加unsafe关键字修饰的方法或使用unsafe { ... }代码块。
3、声明一个整型数组,例如:int[] arr = new int[5] { 10, 20, 30, 40, 50 };
4、使用fixed语句获取首元素地址:fixed (int* ptr = arr) { ... }
5、在fixed块内,ptr即为数组首元素在内存中的实际地址,类型为int*。
二、通过fixed固定字符串并获取字符起始地址
字符串是不可变的托管对象,但fixed支持对其内部字符缓冲区进行固定,从而获得指向第一个字符的指针。注意:字符串内容不可修改,否则引发未定义行为。
1、声明一个字符串变量,例如:string s = "Hello";
2、使用fixed (char* ptr = s) { ... }语法固定字符串。
3、在fixed块内,ptr指向字符串UTF-16编码的第一个char的内存地址。
4、可通过指针算术访问后续字符,如*(ptr + 1)获取第二个字符。
三、使用GCHandle.Alloc固定任意引用类型对象
对于非数组类的托管对象(如自定义类实例),fixed无法直接使用;此时可借助GCHandle的Alloc方法以GCHandleType.Pinned方式固定对象,再通过AddrOfPinnedObject获取其地址。
1、创建目标对象实例,例如:var obj = new MyClass();
2、调用GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
3、在try块中获取地址:IntPtr ptr = handle.AddrOfPinnedObject();
4、ptr即为该对象在堆上的起始内存地址(注意:并非对象字段数据的逻辑起点,而是对象头起始地址)。
5、使用完毕后必须在finally块中调用handle.Free()释放句柄。
四、使用Unsafe.AsPointer获取ref局部变量地址
当已有对托管对象字段或局部变量的ref引用时,可利用System.Runtime.CompilerServices.Unsafe类的AsPointer方法直接获取其地址,无需fixed语句,但要求变量本身生命周期可控且未被优化掉。
1、引入命名空间:using System.Runtime.CompilerServices;
2、声明一个值类型变量并取其引用,例如:int value = 42; ref int r = ref value;
3、调用int* ptr = Unsafe.AsPointer(ref r);
4、ptr即为value变量当前栈地址,适用于局部值类型和结构体字段。
5、注意:对托管引用类型(如class实例)的ref调用AsPointer将返回对象头地址,且该地址在GC发生时可能失效。










