Delphi SizeOf (NativeInt) vs C sizeof (int) on x86-64. Why size difference?

Introduction

So, after a long time running C, I returned to Delphi and found out that there is something new in Delphi. One of them is NativeInt .

To my surprise, I found that Delphi and C process their "native integer" types 1 different for x86-64. Delphi NativeInt seems to behave like a C void * and a Delphi pointer , which is contrary to what I would expect from names.

In Delphi, NativeInt is 64 bits in size. Expressed in code:

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

C has only 64 bit pointers. int remains 32 bits. Expressed in code 2 :

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

Even the Free Pascal 3 compiler agrees with the size of NativeInt .

Question

Why was 64 bit selected for Delphi NativeInt and 32 bit for C int ?

Of course, both are valid in accordance with the documentation / language specification. However, β€œlanguage allows this” is not really a useful answer.

I assume this is due to execution speed, since today it is the main selling point of C. Wikipedia and other sources say that x86-64 have 64-bit operand registers. However, they also state that the default operand size is 32 bits. So is it possible that operations with 64-bit operands are slower compared to 32-bit operands? Or maybe 64-bit registers can perform 2 32-bit operations at the same time? This is the reason?

Perhaps there is another reason why compiler creators chose these sizes?

Footnotes

  • I am comparing Delphi NativeInt to C int because the name / specification assumes they have a similar purpose. I know that there is also a Delphi Integer that behaves like C int on x68 and x86-64 in Delphi.
  • sizeof () returns the size as a multiple of char in C. However, char is 1 byte on x86-64.
  • It does this in Delphi mode and the default mode for NativeInt . Other integer types in default mode represent a whole different worm worm.
+7
c sizeof x86-64 delphi pascal
source share
2 answers

NativeInt is just an integer, the same as a pointer. Hence the fact that it changes size on different platforms. The documentation states that:

The size of NativeInt is equivalent to the size of the pointer on the current platform.

The main use of NativeInt is to store things like operating system descriptors that are actually behind the scenes memory addresses. You should not use it to perform arithmetic operations, store arrays, etc. If you try to do this, it will be much more difficult for you to use the code between the 32-bit and 64-bit versions of your program.

You can think of Delphi NativeInt as a direct equivalent to the .net IntPtr type. In C and C ++, OS descriptor types are usually declared as void* , which is a pointer type, not an integer type. However, you would be fine using a type like intptr_t if you want.

You use the term "native integer" to describe NativeInt , but despite the name, it is very important to understand that NativeInt not a native integer type of language. It will be an Integer . Native in NativeInt refers to the underlying hardware platform, not the language.

The type Delphi Integer , a native integer, matches the type C int , the native type of the corresponding language. And on Windows, these types have 32 bits for 32 and 64-bit systems.

When Windows designers started working with 64-bit Windows, they had a lot of memory about what happened when the int changed from 16 to 32 bits when switching from 16-bit to 32-bit systems. It was not at all fun, although it was the right decision. This time, from 32 to 64, there was no good reason to make an int 64-bit type. If Windows designers did this, it would make porting a lot more difficult. And so they decided to leave int as a 32-bit type.

In terms of performance, the AMD64 architecture was designed to work efficiently with 32-bit types. Since a 32-bit integer is half that of a 64-bit integer, memory usage is reduced, making int only 32 bits in a 64-bit system. And that will have a performance advantage.

A few comments:

  • You state that "C has only 64 bit pointers." This is not true. A 32-bit C compiler typically uses a flat 32-bit memory model with 32-bit pointers.
  • You also say: "in Delphi NativeInt size is 64 bits." Again, this is not so. It can be 32 or 64 bits depending on the target.
+10
source share

Please note that NativeInt is not intended to interact with a pointer!

The problem is that nativeInt is signed.
This is usually not what you want, because the pointer indicates the start of a data block. Negative offsets will have an access violation for you.
If you have a pointer pointing to the middle (because you are doing indexing or something like that), then negative offsets are applied, and NativeInt aka IntPtr appears in the view.

For standard pointers (pointing to the beginning): use UIntPtr because it will not be when the offset becomes greater than 2 ^ 31/2 ^ 63.
(Probably on a 32-bit platform, not on a 64-bit one)

For this reason, there is UIntPtr that exactly matches the C-equivalent. UIntPtr is a NativeUint .

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

A: I want the fastest integer -> Select Int32 aka integer ;
B1: I want to have an integer for pointer arithmetic -> Choose UIntPtr aka NativeUint *.
B2: I index a pointer -> Select IntPtr aka NativeInt .
C: I want a large integer, but I don't want the big slowdown that Int64 gives me on X86 -> select NativeInt. D: I want bigint: select Int64. (but be aware that it will be slow on X86).

*), if you want to make it clear to the reader of your code that you are busy with pointers, you need to call it UIntPtr , obviously.

0
source share

All Articles