C#内存地址和变量

Posted

技术标签:

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

在C#中,有没有办法

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

编辑:

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

【问题讨论】:

【参考方案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);

【讨论】:

以上是关于C#内存地址和变量的主要内容,如果未能解决你的问题,请参考以下文章

C#基础知识总结

C#结构化编程基础(金老师编程课第二讲)

C#方法传参原理(内存地址原理剖析)

C#中使用CAS实现无锁算法

[GO]变量内存和变量地址

Go 如何查看一个变量的内存地址 理解指针问题