【问题标题】:C# Memory address extension with code带有代码的 C# 内存地址扩展
【发布时间】:2018-05-31 06:57:35
【问题描述】:

我得到以下 MemoryAddressExtenstion 代码,它在 C# 中显示对象地址

想知道什么是

IntPtr**

**(IntPtr**)

谢谢

public static class MemoryAddressExtenstion
{
    public static string Address(this object me)
    {
        unsafe
        {
            TypedReference tr = __makeref(me);
            var ptr = **(IntPtr**)&tr;
            return "0x" + ptr.ToString("X");
        }
    }
}

Variable Address

__makeref

TypedReference

Source Code

【问题讨论】:

  • 你从哪里得到的代码?
  • 要了解我建议您查看 C++ 中的指针,这将帮助您理解它,因为它在 C/C++ 中比在 C# 中更常见。 ** 表示指向指针的指针。
  • 如果有人想知道__makeref 到底是什么,请看here
  • 更新了github源码
  • @MatthewWatson,谢谢我也参考了它

标签: c#


【解决方案1】:

遗憾的是,我无法以更好的方式解释它们......所以如果有人可以纠正我,那么谢谢!

在 .NET 中称为“引用”,在 C 中称为“指针”。因此,在 C# 中称为 object me 的内容将是一个 void *me,一个指向对象的指针(称为 me)。

TypedReference tr = __makeref(me);

这会为变量me 创建一个TypedReference。正如我所说的,引用与 C 指针非常相似(事实上,如果你看一下 source code,你会发现它包含一个 IntPtr Value)。所以在 C 中它会是:

void **tr = &me;

(在 .NET 中是指向 TypedReference 的指针,在 C 中是指向指针的指针)

现在代码正在创建第三层间接性

void ***temp = &tr;

然后它会取消引用它两次

void *ptr = **temp;

所以我们有一个*ptr,它与最初的*me 相同。我们这样做是因为通常在 C# 中您不能直接将引用转换为等效指针。

他们为什么要这样做?因为你通常不能转换对指针的引用(你不能(IntPtr)me),也不能直接获取引用的地址(所以你不能&me)。但是你可以创建一个TypedRefence,相当于做&me。现在,很遗憾你不能这样做(IntPtr*)tr(IntPtr)tr。但幸运的是你可以&tr。现在你终于可以得到一个真正的指针了,你可以开始解除对它的引用以返回你想要的初始指针。

【讨论】:

  • 重要的一点是,从__makeref() 返回的TypedReference 结构包含作为其第一个字段的IntPtr,名为Value,它指向感兴趣的对象,因此演员表结束那个值。 (请参阅 the implementation of TypedReference。)如果 Microsoft 通过在 Value 字段前面添加一个新字段来更改 TypedReference 的布局,则转换将不再返回正确的值。
  • @MatthewWatson 是的。我以为是这样的,但我没有看。与往常一样,做无证之事就像在玩命。
【解决方案2】:

**在C/C++中常用来指向一个指针。 IntPtr** 要求一个指向 IntPtr 的指针的指针

**(IntPtr**)&tr; 引用上述结构。

可以在 C# 中使用它们,请参阅 pointers in C#,但我建议您了解更多有关此内容的信息,您应该阅读 C++/C,因为它在这些语言中更常见。

【讨论】:

  • @Amit 是的,我知道 C# 中有指针,但并不常见。这就是为什么我建议阅读 C / C++ 以了解更多关于它的信息,因为它在那里更常见。
  • @Amit 感谢您告诉我,我已将您的链接添加到答案中。
【解决方案3】:

从头开始,IntPtr 就是struct that represents a memory address

类型名后面的* 表示类型是指向前一个类型的指针。所以IntPtr* 表示指向IntPtr 的指针。而IntPtr** 表示指向IntPtr 的指针。

当一个类型在表达式之前的括号中时,它是一个类型转换操作。所以(IntPtr**)x 意味着将x 转换为IntPtr**

& 运算符是address-of operator。所以&tr 表示获取值tr 的地址。因为tr 的类型是TypedReference,所以结果值的类型指针指向TypedReference*

表达式前面的* 运算符是dereference operator,它返回给定地址的值。所以*x是获取x指向的内存位置的值。而**x 是获取指向*x 的内存位置的值。这意味着 x 是指向指针的指针。

但乍一看,这应该会产生垃圾。我们正在从TypedReference* 转换为IntPtr**。它之所以有效,只是因为 first field in a TypedReference is in fact an IntPtr 指向原始对象。

一般来说这是一个危险的小技巧,使用时应该非常小心,因为:

  • 对象的内存地址移动(您可以使用GCHandle.Allocfixed 关键字来固定它们)
  • __makref 是语言中未记录且不受支持的部分
  • 这取决于TypedRef 的实现细节
  • 它不是可移植的代码

【讨论】:

    猜你喜欢
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多