这是一个看起来很简单的问题:
鉴于原生大小的整数最适合算术,为什么C#(或任何其他.NET语言)不支持使用原生大小的IntPtr
和UIntPtr
进行算术?
理想情况下,您可以编写如下代码:
for (IntPtr i = 1; i < arr.Length; i += 2) //arr.Length should also return IntPtr
{
arr[i - 1] += arr[i]; //something random like this
}
这样它就可以在32位和64位平台上运行。 (目前,您必须使用long
。)
编辑:
我不使用这些作为指针(甚至没有提到“指针”这个词)!它们可以被视为MSIL中native int
和C intptr_t
中stdint.h
的C#对应 - 它们是整数,不是指针。
答案 0 :(得分:6)
在.NET 4中,类型IntPtr
的左手操作数与整数类型的右手操作数(int
,long
等)之间的算术是支持。
[编辑]:
正如其他人所说,它们被设计为用本地语言表示指针(如IntPtr这个名称所暗示的那样)。声称你使用它们作为本机整数而不是指针是很好的,但是你不能忽视整数的原始大小重要的一个主要原因是用作指针。如果您正在执行数学运算或独立于运行代码的处理器和内存体系结构的其他常规函数,那么使用int
和{{1}等类型可能会更有用和直观。无论硬件如何,你知道它们的固定大小以及每种情况下的上限和下限。
正如类型long
旨在表示本机指针一样,算术运算旨在表示您将对指针执行的逻辑数学运算:向本机指针添加一些整数偏移以达到新的本地指针(不支持添加两个IntPtr
,也不支持使用IntPtr
作为右手操作数。)
答案 1 :(得分:6)
也许原生大小的整数可用于最快算术,但它们肯定不能用于最无错误的程序。
就我个人而言,讨厌使用整数类型进行编程,当我坐下来开始输入时,我不知道它们的大小(我在看着你,C ++),我绝对宁愿安心CLR类型为您提供了使用针对平台定制的CPU指令可能提供的非常可疑且肯定有条件的性能优势。
还要考虑JIT编译器可以优化运行该进程的体系结构,而“常规”编译器必须生成机器代码而无需访问此信息。因此,JIT编译器可能会以更快的速度生成代码。
我想我并不是唯一一个想到这一点的人,所以这可能是有原因的。
答案 2 :(得分:1)
我实际上可以想到IntPtr(或UIntPtr)有用的一个原因:访问数组元素需要原生大小的整数。虽然本机整数从不暴露给程序员,但它们在IL内部使用。像C#中some_array[index]
这样的东西实际上会在IL中编译为some_array[(int)checked((IntPtr)index)]
。在用ILSpy反汇编我自己的代码后,我注意到了这一点。 (index
变量在我的代码中是64位。)为了验证反汇编程序没有出错,Microsoft自己的ILDASM工具显示存在conv.u
和conv.i
指令我的集会。这些指令将整数转换为系统的本机表示。我不知道IL代码中所有这些转换指令的性能含义是什么,但希望JIT足够智能以优化性能损失;如果没有,那么下一个最好的事情就是允许在没有转换的情况下操纵本机整数(在我看来,这可能是使用本机类型的主要动机)。
目前,F#语言允许使用nativeint
及其无符号对应物进行算术运算。但是,数组只能在F#中由int
索引,这意味着nativeint
对于索引数组的目的不是很有用。
如果它真的困扰你那么多,编写自己的编译器,解除对本机整数使用的限制,创建自己的语言,在IL中编写代码,或在编译后调整IL。就个人而言,我认为通过使用native int来挤出额外的性能或节省内存是个坏主意。如果您希望您的代码像手套一样适合系统,那么最好使用支持处理器内在函数的低级语言。
答案 3 :(得分:0)
.Net Framework尝试不引入无法解释的操作。即没有DateTime + DateTime,因为没有2个日期之和的概念。相同的推理适用于指针类型 - 没有2个指针之和的概念。 IntPtr作为平台依赖int值存储的事实并不重要 - 有很多其他类型在内部存储为基本值(同样DateTime可以表示为long)。
答案 4 :(得分:-1)
因为这不是处理内存寻址的“安全”方式。指针算法可能导致C#明确设计避免的各种错误和内存寻址问题。