问题 IntPtr和UIntPtr之间的区别


我正在查看P / Invoke声明 RegOpenKeyEx 当我在页面上注意到这条评论时:

IntPtr 至 UIntPtr:调用时 IntPtr 对于句柄,您将遇到溢出。 UIntPtr 如果您希望在32位和64位平台上正常工作,则是正确的选择。

这对我来说没有多大意义:两者 IntPtr 和 UIntPtr 应该表示指针,因此它们的大小应该匹配OS的位数 - 32位或64位。由于这些不是数字而是指针,因此它们的带符号数值应该无关紧要,只有表示它们指向的地址的位。我想不出为什么这两者之间存在差异的原因,但这条评论让我不确定。

是否有特定的使用理由 UIntPtr 代替 IntPtr?根据 文件

IntPtr 类型符合CLS,而 UIntPtr 类型不是。只有 IntPtr type用于公共语言运行库。该 UIntPtr 提供类型主要是为了保持建筑的对称性 IntPtr 类型。

当然,这意味着没有区别(只要有人不尝试将值转换为整数)。那么来自pinvoke.net的上述评论是不正确的?

编辑

看完之后 MarkH的答案,我做了一些检查,发现.NET应用程序不是大地址感知,并且在32位模式下编译时只能处理2GB的虚拟地址空间。 (可以用一个  打开大地址识别标志,但MarkH的答案显示.NET Framework内部的检查会破坏,因为假设地址空间只有2GB,而不是3GB。)

这意味着指针可以拥有的所有正确的虚拟内存地址(就.NET Framework而言)将介于0x00000000和0x7FFFFFFF之间。当此范围转换为签名时 int,没有值是负数,因为没有设置最高位。这加强了我的信念,即使用IntPtr与UIntPtr没有区别。我的推理是否正确?


12682
2017-11-01 04:05


起源

看看这里。 stackoverflow.com/questions/1320296/intptr-vs-uintptr - David J
@ Fermat2357是的,这是引发我输入新问题的问题。 - xxbbcc
我认为你是对的。完全没有区别。一个 HANDLE 它只是资源的唯一编号(当然在Win32中,它通常实现为指向内存区域的32位指针)。然而, IntPtr 能够嵌入任何类型的32位数。与UIntPtr的区别仅在于对这个数字的解释。 - David J


答案:


UIntPtr 和 IntPtr 内部实施为

private unsafe void* m_value;

你是对的,只是管理代表地址的位。

我能想到溢出问题的唯一方法就是你尝试执行指针算术。这两个类都支持添加和减去偏移量。但在这种情况下,二进制表示在这样的操作之后应该是正常的。

根据我的经验,我也更喜欢 UIntPtr,因为我认为指针作为无符号对象。但这不相关,只有我的意见。

如果你使用它似乎没有任何区别 IntPtr 要么 UIntPtr 在你的情况下。

编辑:

IntPtr 是CLS兼容的,因为CLR之上的语言不支持unsigned。


9
2017-11-01 05:05



我发现微软做的这种框架设计非常令人不安 - 为了对称而有一个重复的无意义类型来包装指针。他们应该只创建一个包装器并将其命名为SafePtr,而不是进入签名/未签名的区域,这根本没有意义。 - xxbbcc


当然,这意味着没有区别(只要有人不尝试将值转换为整数)。

不幸的是,框架试图正是这样做的(当专门为x86编译时)。这俩 IntPtr(long) 构造函数,以及 ToInt32() 方法尝试将值转换为 int 在一个 checked 表达。这是使用框架调试符号时看到的实现。

    public unsafe IntPtr(long value)
    { 
        #if WIN32
            m_value = (void *)checked((int)value);
        #else
            m_value = (void *)value; 
        #endif
    } 

当然,如果值超出范围,则checked表达式将抛出异常。该 UIntPtr 不会溢出相同的值,因为它试图转换为 uint 代替。


4
2017-11-01 05:43



我想知道是否存在该检查,因为用户虚拟地址空间从0x00000000变为0x7FFFFFFF。由于位31未设置在有效的虚拟内存地址中,因此 checked 转换不应该在32位操作系统上失败。我不确定.NET是否支持/ 3GB,因为它扩展了虚拟地址空间。 - xxbbcc
有两个ctors。一 public IntPtr(long value)第二个 public IntPtr(int value)。它完全正常(对于32位窗口),第一个ctor只接受64位值,可以将其转换为32位而不会丢失精度。 - David J
@ Fermat2357是的,那是对的但是 checked 转换不是关于精度损失,而是关于验证地址是否在有效地址范围内。理论上,检查不应该在32位系统上失败,因为不应该在用户空间中设置最高位。 - xxbbcc
我刚检查过,.NET应用程序不是大地址识别(一个人只能在标志中使用 editbin 所以我想以上 checked 即使操作系统运行,转换也不应该在32位系统上失败 /3GB  - 由于应用程序不是大地址识别,它将获得常规的2GB虚拟地址空间。 - xxbbcc
该 UIntPtr 不会溢出相同的值,因为它试图转换为 uint 代替。 这不是问题,除非您使用 ctor 用一个 long 不适合 int。顺便一提 UIntPtr也会溢出来。它以同样的方式实现了 checked 在签名案件中的陈述。 - David J


IntPtr \ UIntPtr之间的差异与以下各项之间的差异相同:Int32 \ UInt32(即,所有这些都是关于如何解释数字的。)

通常情况下,您选择哪个并不重要,但如上所述,在某些情况下,它可能会回来咬你。

(我不确定为什么MS选择IntPtr开始(CLS Compliance等),内存被处理为DWORD(u32),这意味着它是无符号的,因此,首选方法应该是UIntPtr,而不是IntPtr,对吧? )

甚至:UIntPtr.Add()

对我来说似乎不对,它需要一个UIntPtr作为指针,而一个'int'作为偏移量。 (对我而言,'uint'会更有意义。为什么将一个有符号的值输入到无符号方法中,何时,代码很可能将它转换为'uint'。/ facepalm)

我个人更喜欢UIntPtr而不是IntPtr,因为无符号值与我正在使用的底层内存的值相匹配。 :)


顺便说一句,我可能最终会创建自己的指针类型(使用UInt32),专门用于直接处理内存。 (我猜测UIntPtr不会捕获所有可能的坏内存问题,即0xBADF00D等等。这是一个等待发生的CTD ......我将不得不看看内置类型如何首先处理事情,希望是null \ zero检查是否正确过滤掉这样的东西。)


-1
2018-06-30 09:40