【问题标题】:C# memory address and variableC#内存地址和变量
【发布时间】:2010-10-09 23:36:15
【问题描述】:

在C#中,有没有办法

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

编辑:

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

【问题讨论】:

    标签: c# .net memory-management


    【解决方案1】:

    对于#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();
        }
    }
    

    【讨论】:

    • #2,这很酷。但是如何将内存地址作为字符串获取。说int i; int* pi = &i;我无法使用 Console.WriteLine(pi); 打印它我希望打印 0x0439ecc4 之类的内容。有什么线索吗?
    • Console.WriteLine("0x" + pi.ToString("x") 将打印十六进制值
    • @Hamish, pi.ToString("x") 引发编译错误。 “操作员 '。'不能应用于“int*”类型的操作数”
    • 您需要使用“->”运算符。 pi->ToString()
    • @rism,OP 希望将指针的值重新解释为整数,而不是指针指向的值。将int* 转换为IntPtr 允许它。
    【解决方案2】:

    数字 1 根本不可能,您不能拥有指向托管对象的指针。但是,您可以使用 IntPtr 结构来获取有关引用中指针地址的信息:

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

    对于数字 2,您使用 & 运算符:

    int* p = &myIntVariable;
    

    指针当然必须在不安全的块中完成,并且您必须在项目设置中允许不安全的代码。如果变量是方法中的局部变量,它是在堆栈上分配的,因此它已经固定,但如果变量是对象的成员,则必须使用 fixed 关键字将该对象固定在内存中,这样它就不会由垃圾收集器移动。

    【讨论】:

    • 很奇怪,为什么代码不能用 string str = "hello" 编译;字符串* ptr = str; ?有什么想法吗?
    • 我的错,这实际上根本不可能。你不能有一个指向托管对象的指针,你必须使用一个 IntPtr。
    • 为什么投反对票?如果你不说你不喜欢什么,那是毫无意义的......
    • GCHandle.ToIntPtr 返回句柄本身的内部表示,而不是它指向的对象的地址。如果为同一个对象创建多个句柄,GCHandle.ToIntPtr 将为每个句柄返回不同的结果。 GCHandle.AddrOfPinnedObject 返回句柄指向的对象的地址。详情请见GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject
    【解决方案3】:

    最正确地回答您的问题:

    #1 是可能的,但有点棘手,应该仅出于调试原因进行:

    object o = new object();
    TypedReference tr = __makeref(o);
    IntPtr ptr = **(IntPtr**)(&tr);
    

    这适用于任何对象,并且实际上返回指向内存中对象的内部指针。请记住,由于 GC 喜欢在内存中移动对象,因此地址可以随时更改。

    #2 指针不是对象,因此不会从它们继承 ToString。您必须像这样将指针转换为 IntPtr

    Console.WriteLine((IntPtr)pi);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 2021-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多