Delphi SizeOf(NativeInt) vs C sizeof (int) на x86-64. Почему разница в размере?

предисловие

Итак, после долгого времени работы только на C я вернулся в Дельфы и узнал, что в Дельфах есть некоторые новые вещи. Один NativeInt.

к моему удивлению, я обнаружил, что Delphi и C обрабатывают свое "собственное целое число"1 типы отличаются для x86-64. Delphi NativeInt кажется, ведет себя как недействительным с * и Указатель Delphi что противоречит тому, что я ожидаю от имена.

на Delphi NativeInt имеет размер 64 бит. Выражено в коде:

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

C имеет только 64-битные указатели. int остается 32-разрядной. Выражено в коде2:

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

даже бесплатный компилятор Pascal3 соглашается на размер NativeInt.

вопрос

Почему 64 бит был выбран для Delphi NativeInt и 32 бита для C int?

конечно оба действительны согласно документации/спецификации языка. Однако "язык позволяет это" на самом деле не является полезным ответом.

Я думаю, это связано со скоростью выполнения, так как это основная точка продажи C сегодня. Википедия и другие источники говорят, что x86-64 имеют 64-битные регистры операндов. Однако они также утверждают, что размер операнда по умолчанию составляет 32 бита. Поэтому, возможно, операции с 64-битными операндами медленнее по сравнению с 32-битные операнды? Или, может быть, 64-битные регистры могут выполнять 2 32-битные операции одновременно? Это причина?

может быть, есть еще одна причина, по которой создатели компиляторов выбрали эти размеры?

сноски

  1. я сравниваю Delphi NativeInt to C int потому что имя / specificaion предполагает, что они имеют аналогичную цель. Я знаю, что есть еще Delphi Integer, который ведет себя как C int на x68 и x86-64 в Delphi.
  2. sizeof () возвращает размер, кратный char В С. char - 1 байт на x86-64.
  3. он делает это в режиме Delphi и режиме по умолчанию для NativeInt. Другие целочисленные типы в режиме по умолчанию куча.

2 ответов


NativeInt - Это просто целое число, которое имеет тот же размер, как указатель. Отсюда и то, что он меняет размеры на разных платформах. The документация говорит именно об этом:

размер NativeInt эквивалентен размеру указателя на текущей платформе.

основное использование для NativeInt хранить такие вещи, как дескрипторы операционной системы, которые за кулисами на самом деле являются адресами памяти. Вы не должны использовать это для выполнения арифметики, хранения длины массива и т. д. Если вы попытаетесь это сделать, вам будет намного сложнее обмениваться кодом между 32 и 64 битными версиями вашей программы.

вы можете думать о Delphi NativeInt как прямой эквивалент типа .net IntPtr. В C и c++ типы дескрипторов ОС обычно объявляются как void* который является типом указателя, а не целочисленный тип. Однако, вы бы прекрасно использовать типа intptr_t если вы так пожелавший.

вы используете термин "собственное целое число" для описания NativeInt, но, несмотря на название, очень важно понять, что NativeInt не родной типа integer языка. Это было бы Integer. Туземец в NativeInt относится к базовой аппаратной платформе, а не к языку.

тип Delphi Integer, родное целое число языка, совпадает с типом C int соответствующий язык родной тип. И на Windows эти типы есть 32-битные для 32 и 64 битных систем.

когда дизайнеры Windows начали работать над 64-битными окнами, у них была острая память о том, что произошло, когда int изменено с 16 до 32 бит при переходе с 16 бит на 32 битные системы. Это было совсем не весело, хотя решение явно было правильным. На этот раз, с 32 до 64, не было никаких веских причин, чтобы сделать int Тип 64 бит. Если бы дизайнеры Windows сделали это, это сделало бы перенос много тяжелая работа. И поэтому они решили уйти int как 32-битный тип.

С точки зрения производительности архитектура AMD64 была разработана для эффективной работы с 32-битными типами. Поскольку 32-разрядное целое число вдвое меньше 64-разрядного целого числа, то использование памяти уменьшается, делая int только 32 бита в 64-битной системе. И это будет иметь преимущество в производительности.

пара комментариев:

  • вы заявляете ,что"C имеет только 64-битные указатели". То есть не очень. 32-битный компилятор C обычно использует плоскую 32-битную модель памяти с 32-битными указателями.
  • вы также говорите: "в Дельфах NativeInt имеет размер 64 бит". Опять же, это не так. Он имеет ширину 32 или 64 бита в зависимости от цели.

обратите внимание, что NativeInt составляет не предназначены для взаимодействия с указателем!

проблема в том, что nativeint подписан.
Обычно это не то, что вы хотите, потому что указатель указывает на начало блока данных. отрицательные смещения приведут к нарушению доступа здесь.
Если у вас есть указатель, указывающий на середину (потому что вы делаете индексирование или что-то в этом роде), то применяются отрицательные смещения и NativeInt он же IntPtr попадает в поле зрения.

для стандартных указателей (указывающих на начало): используйте UIntPtr, потому что он будет не срыв, когда смещение становится больше, чем 2^31/2^63.
(вероятно, на платформе 32bit, не столько на 64бит)

по этой причине существует UIntPtr, который точно соответствует эквиваленту C.
The UIntPtr это NativeUint.

варианты использования
Какой из типов выбрать зависит от варианта использования.

A: я хочу самое быстрое целое число - > выберите Int32 aka integer;
B1: я хочу иметь целое число для выполнения арифметики указателя - > выбрать UIntPtr ака NativeUInt*.
B2: я делаю индексирование с помощью указателя - > выберите IntPtr ака NativeInt.
C: я хочу большое целое число, но не хочу большого замедления, которое Int64 дает мне на X86 -> выберите NativeInt. D: Я хочу bigint: выберите Int64. (но знайте, что это будет медленно X86).

*) если вы хотите, чтобы читатель Вашего кода понял, что вы возитесь с указателями, вам нужно назвать его UIntPtr очевидно.