7

前言

所以在做了很长一段时间的纯C工作后,我回到Delphi,发现Delphi中有一些新东西。一个是NativeInt

令我惊讶的是,我发现 Delphi 和 C 处理它们的“本机整数” 1类型对于 x86-64 是不同的。Delphi NativeInt似乎表现得像C void *Delphi Pointer,这与我对名称的期望相反。

Delphi NativeInt的大小为 64 位。用代码表示:

SizeOf(NativeInt) = SizeOf(Pointer) = SizeOf(Int64) = 8

C只有64位指针。int仍然是 32 位。在代码2中表示:

sizeof(int) == 4 != sizeof(void *) == 8

甚至 Free Pascal Compiler 3也同意NativeInt的大小。

问题

为什么Delphi NativeInt选择 64 位而C int选择32 位?

当然,根据语言文档/规范,两者都是有效的。但是,“语言允许”并不是一个真正有用的答案。

我想这与执行速度有关,因为这是当今 C 语言的主要卖点。维基百科和其他来源都说 x86-64 确实有 64 位操作数寄存器。但是,它们还声明默认操作数大小为 32 位。那么,与 32 位操作数相比,对 64 位操作数的操作可能会更慢?或者也许 64 位寄存器可以同时进行 2 个 32 位操作?这是一个原因吗?

编译器的创建者选择这些尺寸是否还有其他原因?

脚注

  1. 我将Delphi NativeIntC int进行比较,因为名称/规范表明它们具有相似的目的。我知道还有Delphi Integer,它的行为类似于Delphi中 x68 和 x86-64 上的C int 。
  2. sizeof() 在 C 中将大小作为char的倍数返回。但是,char在 x86-64 上是 1 个字节。
  3. 它在 Delphi 模式和NativeInt的默认模式下这样做。默认模式下的其他整数类型完全是另一种蠕虫。
4

2 回答 2

10

NativeInt只是一个与指针大小相同的整数。因此,它会在不同平台上改变大小。该文档确切地说:

NativeInt 的大小相当于当前平台上指针的大小。

的主要用途NativeInt是存储诸如操作系统句柄之类的东西,这些东西在幕后实际上是内存地址。您不应该使用它来执行算术、存储数组长度等。如果您尝试这样做,那么在您的程序的 32 位和 64 位版本之间共享代码会变得更加困难。

您可以认为 DelphiNativeInt直接等同于 .net 类型IntPtr。在 C 和 C++ 中,操作系统句柄类型通常被声明为void*指针类型而不是整数类型。但是,如果您愿意,您最好使用类似的类型intptr_t

您使用术语“本机整数”来描述NativeInt,但尽管名称如此,但意识到这NativeInt不是语言的本机整数类型非常重要。那将是Integer。native inNativeInt指的是底层硬件平台,而不是语言。

Delphi type Integer,语言原生整数,与 C type int,相应的语言原生类型相匹配。在 Windows 上,这些类型对于 32 位和 64 位系统都是 32 位宽。

int当 Windows 设计人员开始开发 64 位 Windows 时,他们对从 16 位到 32 位系统从 16 位系统过渡到 32 位系统时发生的事情有着敏锐的记忆。这一点都不好玩,尽管这显然是一个正确的决定。这一次,从 32 到 64,没有令人信服的理由来制作int64 位类型。如果 Windows 设计人员这样做了,移植工作就会变得更加困难。所以他们选择离开int32 位类型。

在性能方面,AMD64 架构设计用于在 32 位类型上高效运行。int由于 32 位整数是 64 位整数大小的一半,因此在 64 位系统上仅使用 32 位可以减少内存使用量。这将带来性能优势。

一些评论:

  • 您声明“C 只有 64 位指针”。事实并非如此。32 位 C 编译器通常会使用带有 32 位指针的平面 32 位内存模型。
  • 您还说,“在 DelphiNativeInt中是 64 位大小”。又不是这样。它是 32 位或 64 位宽,具体取决于目标。
于 2013-09-12T18:13:24.783 回答
0

Note that NativeInt is not meant to interact with a pointer!

The issue is that nativeInt is signed.
Normally this is not what you want, because the pointer points the the beginning of the datablock. Negative offsets will net you an access violation here.
If you have a pointer pointing to the middle (because you're doing indexing or something like that) then negative offsets apply and NativeInt aka IntPtr comes into view.

For standard pointers (pointing to the start): use UIntPtr, because it will not breakdown when the offset becomes bigger than 2^31/2^63.
(Likely on a 32bit platform, not so much on 64bit)

For this reason there is a UIntPtr, which maps exactly to the C equivalent.
The UIntPtr is a NativeUint.

Use cases
Which of the types you choose to use depends on the use case.

A: I want the fastest integer -> Choose Int32 aka integer;
B1: I want to have an integer to do pointer arithmetic -> Choose UIntPtr aka NativeUInt*.
B2: I do indexing with my pointer -> Choose IntPtr aka NativeInt.
C: I want a big integer, but don't want the big slowdown that Int64 gives me on X86 -> choose NativeInt. D: I want a bigint: choose Int64. (but know that it will be slowish on X86).

*) if you want to make it clear to the reader of your code that you're messing with pointers you need to name it UIntPtr obviously.

于 2013-09-12T20:02:25.160 回答