C#内存地址和变量

26

在C#中,是否有一种方法可以:

  1. 获取存储在引用类型变量中的内存地址?
  2. 获取变量的内存地址?

编辑:

int i;
int* pi = &i;
  • 如何打印出 pi 的十六进制值?
3个回答

17
对于问题 #2,& 操作符的工作方式与 C 中相同。如果变量不在堆栈上,则可能需要使用 fixed 语句将其固定在您操作期间,以防垃圾收集器将其移动。
对于问题 #1,引用类型更加棘手:您需要使用 GCHandle,并且引用类型必须是可平坦化的,即必须有定义的内存布局并且可以按位复制。
为了将地址作为数字访问,您可以从指针类型转换为 IntPtr(一个定义为与指针大小相同的整数类型),然后转换为 uintulong(取决于底层机器的指针大小)。
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
class Blittable
{
    int x;
}

class Program
{
    public static unsafe void Main()
    {
        int i;
        object o = new Blittable();
        int* ptr = &i;
        IntPtr addr = (IntPtr)ptr;

        Console.WriteLine(addr.ToString("x"));

        GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
        addr = h.AddrOfPinnedObject();
        Console.WriteLine(addr.ToString("x"));

        h.Free();
    }
}

1
#2,很酷。但是你如何将内存地址作为字符串获取呢?比如说int i; int* pi = &i; 我无法使用Console.WriteLine(pi)打印它。我期望像0x0439ecc4这样的东西被打印出来。有什么线索吗? - Ray Lu
Console.WriteLine("0x" + pi.ToString("x")) 将打印十六进制值。 - Hamish Smith
@Hamish,pi.ToString("x") 会引发编译错误。"运算符'.'不能应用于类型'int *'的操作数"。 - Ray Lu
你需要使用“->”运算符。pi->ToString() - rism
1
@rism,OP想要将指针的值重新解释为整数,而不是由指针所指向的值。 将int *强制转换为IntPtr即可实现。 - Jeffrey Hantin

5
为了最准确地回答您的问题:
#1 可能,但有点棘手,应该只在调试时进行:
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);

这适用于任何对象,实际上返回内存中对象的内部指针。请记住,由于GC喜欢在内存中移动对象,地址随时可能发生变化。
#2 指针不是对象,因此不能从对象继承ToString。您需要将指针转换为IntPtr,如下所示:
Console.WriteLine((IntPtr)pi);

5

第一点根本不可能实现,你不能拥有指向托管对象的指针。但是,你可以使用IntPtr结构来获取有关引用中指针地址的信息:

GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);
IntPtr pointer = GCHandle.ToIntPtr(handle);
string pointerDisplay = pointer.ToString();
handle.Free();

对于第二个数字,您可以使用&运算符:

int* p = &myIntVariable;

当涉及到指针时,必须在不安全的块中进行操作,并且需要在项目设置中允许使用不安全代码。如果变量是方法中的局部变量,则已经分配在堆栈上,因此已经固定,但如果变量是对象的成员,则需要使用fixed关键字将该对象固定在内存中,以防止被垃圾收集器移动。


奇怪啊,为什么代码无法编译,字符串 str = "hello"; string* ptr = str; 有任何想法吗? - Ray Lu
我的错,实际上这是不可能的。你不能有一个指向托管对象的指针,你必须使用IntPtr。 - Guffa
6
为什么要踩?如果你不说出你不喜欢的原因,那就毫无意义了... - Guffa
GCHandle.ToIntPtr返回句柄本身的内部表示,而不是它所指向的对象的地址。如果为同一对象创建多个句柄,则GCHandle.ToIntPtr将为每个句柄返回不同的结果。GCHandle.AddrOfPinnedObject返回句柄所指向的对象的地址。有关详细信息,请参阅GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject - Antosha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接