当我在页面上发现此评论时,我正在查看RegOpenKeyEx
的P / Invoke声明:
将
IntPtr
更改为UIntPtr
:当使用IntPtr
调用句柄时,您将遇到溢出。如果您希望在32位和64位平台上正常工作,UIntPtr
是正确的选择。
这对我来说没有多大意义:IntPtr
和UIntPtr
都应该表示指针,因此它们的大小应该与OS的位数相匹配 - 32位或64位。由于这些不是数字而是指针,因此它们的带符号数值应该无关紧要,只有表示它们指向的地址的位。我想不出为什么这两者之间会有区别,但这条评论让我不确定。
使用UIntPtr
代替IntPtr
是否有特定原因?根据{{3}}:
IntPtr
类型符合CLS,而UIntPtr
类型则不符合。公共语言运行库中仅使用IntPtr
类型。提供UIntPtr
类型主要是为了保持IntPtr
类型的架构对称性。
这当然意味着没有区别(只要有人不尝试将值转换为整数)。所以pinvoke.net上面的评论不正确吗?
修改:
在阅读documentation之后,我做了一些检查,发现.NET应用程序不是大地址感知,并且在32位模式下编译时只能处理2GB的虚拟地址空间。 (可以使用MarkH's answer打开大地址识别标志,但MarkH的答案显示.NET Framework内的检查会破坏因为地址空间仅为2GB,而不是3GB。)
这意味着指针可以拥有的所有正确的虚拟内存地址(就.NET Framework而言)将介于0x00000000和0x7FFFFFFF之间。当此范围转换为带符号的 int
时,没有值为负值,因为未设置最高位。这强化了我的信念,即使用IntPtr与UIntPtr没有区别。我的推理是否正确?
Fermat2357指出上述编辑错误。
答案 0 :(得分:9)
UIntPtr
和IntPtr
内部实施为
private unsafe void* m_value;
你是对的,只是管理代表地址的位。
我唯一可以考虑溢出问题的是你尝试执行指针算术。这两个类都支持添加和减去偏移量。但是在这种情况下,二进制表示在这样的操作之后应该没问题。
根据我的经验,我也更喜欢UIntPtr
,因为我认为指针是无符号对象。但这不相关,只有我的意见。
如果您在案件中使用IntPtr
或UIntPtr
,似乎没有任何区别。
编辑:
IntPtr
符合CLS,因为CLR之上的语言不支持unsigned。
答案 1 :(得分:4)
当然,这意味着没有区别(只要有人不尝试将值转换为整数)。
不幸的是,框架试图正是这样做的(当专门为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
。
答案 2 :(得分:0)
IntPtr \ UIntPtr之间的差异与以下各项之间的差异相同:Int32 \ UInt32(即,所有这些都是关于如何解释数字的。)
通常情况下,选择哪个并不重要,但正如所提到的,在某些情况下,它可能会再次咬你。
(我不确定为什么MS选择IntPtr开始(CLS Compliance等),内存被处理为DWORD(u32),这意味着它是无符号的,因此,首选方法应该是UIntPtr,而不是IntPtr ,对吧?)
Even:UIntPtr.Add()
对我来说似乎不对,它需要一个UIntPtr作为指针,而一个'int'作为偏移量。 (对我来说,'uint'会更有意义。为什么将一个有符号的值输入到一个无符号方法中,当代码很可能将它转换为'uint'时。/ facepalm)
我个人更喜欢UIntPtr而非IntPtr只是因为无符号值与我正在使用的底层内存的值相匹配。 :)
顺便说一句,我可能最终会创建自己的指针类型(使用UInt32),专门用于直接处理内存。 (我猜测UIntPtr不会捕获所有可能的坏内存问题,即0xBADF00D等等。这是一个等待发生的CTD ......我将不得不看看内置类型是怎样的首先处理事情,希望null \ zero检查正确地过滤掉这样的东西。)