我正在查看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没有区别。我的推理是否正确?
UIntPtr
和 IntPtr
内部实施为
private unsafe void* m_value;
你是对的,只是管理代表地址的位。
我能想到溢出问题的唯一方法就是你尝试执行指针算术。这两个类都支持添加和减去偏移量。但在这种情况下,二进制表示在这样的操作之后应该是正常的。
根据我的经验,我也更喜欢 UIntPtr
,因为我认为指针作为无符号对象。但这不相关,只有我的意见。
如果你使用它似乎没有任何区别 IntPtr
要么 UIntPtr
在你的情况下。
编辑:
IntPtr
是CLS兼容的,因为CLR之上的语言不支持unsigned。
当然,这意味着没有区别(只要有人不尝试将值转换为整数)。
不幸的是,框架试图正是这样做的(当专门为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
代替。
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检查是否正确过滤掉这样的东西。)