Разве на простые 64-битные сломается много разумного кода?
-
14-10-2019 - |
Вопрос
До недавнего времени я рассматривал решение большинства системных импеллеров/поставщиков int
32-битный даже на 64-битных машинах своего рода целесообразной бородавки. С современными типами фиксированного размера C99 (int32_t
а также uint32_t
, и т. Д.) Необходимость, чтобы быть стандартным целочисленным типом каждого размера 8, 16, 32 и 64 в основном исчезает, и это похоже на int
С таким же успехом можно было бы сделать 64-битный.
Однако самое большое реальное следствие размера простого int
в C происходит от того факта, что C, по сути, не имеет арифметики на меньшем, чемint
типы В частности, если int
больше 32-битного, в результате любой арифметики на uint32_t
Значения имеют тип signed int
, что довольно тревожно.
Это веская причина сохранить int
Постоянно исправлено на 32-битной реализации в реальном мире? Я склоняюсь к тому, чтобы сказать да. Мне кажется, что может быть огромный класс использования uint32_t
который сломается, когда int
больше 32 бит. Даже применение уникального минус или битового оператора комплемента становится опасным, если вы не отбросите обратно в uint32_t
.
Конечно, те же проблемы применяются к uint16_t
а также uint8_t
При текущих реализациях, но все, кажется, знают и привыкли относиться к ним как «меньше, чемint
"Типы.
Решение
Как вы говорите, я думаю, что правила продвижения по службе действительно являются убийцей. uint32_t
тогда продвигал int
И вдруг вы бы подписали арифметику, где почти все ожидают без подписи.
Это было бы в основном скрыто в местах, где вы просто арифметику и обратно обратно uint32_t
. Анкет Но это может быть смертельным в местах, где вы сравниваете константы. Является ли код, который зависит от таких сравнений без явного состава, разумно, я не знаю. Кастинг постоянных, как (uint32_t)1
может стать довольно утомительным. Я лично, по крайней мере, всегда использую суффикс U
Для постоянных, которые я хочу быть без подписи, но это уже не так читаемо, как мне хотелось бы.
Также имеют в виду, что uint32_t
и т. д. не гарантированно. Даже не uint8_t
. Анкет Применение этого является расширением от POSIX. Таким образом, в этом смысле C как язык далеко не способен сделать этот шаг.
Другие советы
"Разумный код" ...
Ну ... вещь о разработке, вы пишете и исправляете это, а затем это работает ... и затем вы останавливаетесь!
И, может быть, вас много сгорели, поэтому вы остаетесь хорошо в безопасных диапазонах определенных функций, и, возможно, вы не были сожжены в такой конкретный путь Таким образом, вы не понимаете, что полагаетесь на что-то, что может измениться.
Или даже то, что вы полагаетесь на ошибку.
На компиляторах Olden Mac 68000 INT был 16 -битный, а длинный был 32. Но даже тогда большинство существующих C -кода предполагал, что INT составлял 32, поэтому типичный код, который вы обнаружили в группе новостей, не сработает. (О, и у Mac не было Printf, но я отступаю.)
Итак, я получаю, да, если вы измените что-либо, тогда некоторые вещи сломаются.
С современными типами фиксированного размера C99 (Int32_T и UINT32_T и т. Д.) Необходимость, чтобы быть стандартным целочисленным типом каждого размера 8, 16, 32 и 64 в основном исчезает,
C99 имеет типы фиксированного размера, а не типы фиксированного размера. Нативные типы целочисленных целого числа все еще char
, short
, int
, long
, а также long long
. Анкет Они все еще актуальны.
Проблема с ILP64 заключается в том, что он имеет большое несоответствие между типами C и C99 Typedefs.
- int8_t = char
- int16_t = короткий
- int32_t = Нестандартный тип
- int64_t = int, длинный или длинный
Из 64-битные модели программирования: почему LP64?:
К сожалению, модель ILP64 не предоставляет естественный способ описания 32-разрядных типов данных и должна прибегнуть к невоздумым конструкциям, таким как
__int32
Чтобы описать такие типы. Это может вызвать практические проблемы при производстве кода, который может работать как на 32, так и 64 -битных платформах без#ifdef
конструкции. Было возможно переносить большое количество кода в модели LP64 без необходимости вносить такие изменения, сохраняя при этом инвестиции, сделанные в наборах данных, даже в тех случаях, когда информация о типинге не была сделана извне приложением.
Dec Alpha и OSF/1 Unix были одной из первых 64-битных версий UNIX, и он использовал 64-разрядные целые числа-архитектура ILP64 (значение int
, long
и все указатели были 64-разрядными количествами). Это вызвало много проблем.
Одна проблема, которую я не видел, упомянут - поэтому я вообще отвечаю так долго - это то, что если у вас есть 64 -битная int
, какой размер вы используете для short
? Оба 16 бит (классические, ничего не подходят) и 32 бита (хорошо, хорошо, short
должно быть вдвое меньше, чем int
'Подход) представит некоторые проблемы.
С C99 <stdint.h>
а также <inttypes.h>
Заголовок, вы можете кодировать целыми числам фиксированного размера-если вы решите игнорировать машины с 36-битными или 60-битными целыми числами (что, по крайней мере, квази-регитиматическое). Тем не менее, большая часть кода не написана с использованием этих типов, и в коде, как правило, существуют глубоко укоренившиеся и в значительной степени скрытые (но принципиально ошибочные) предположения, которые будут расстроены, если модель отходит от существующих вариаций.
Обратите внимание на модель Microsoft Ultra-Conservative LLP64 для 64-битных Windows. Это было выбрано, потому что слишком много старого кода сломался бы, если бы 32-разрядная модель была изменена. Тем не менее, код, который был перенесен в архитектуру ILP64 или LP64, не был сразу же переносимы в LLP64 из -за различий. Теоретики заговора, вероятно, скажут, что он был намеренно выбрано, чтобы затруднить то, чтобы код, написанный для 64-разрядного UNIX, был перенесен в 64-разрядные окна. На практике я сомневаюсь, что это было больше, чем счастливый (для Microsoft) побочный эффект; 32-разрядный код Windows должен был быть пересмотрен, чтобы использовать модель LP64.
Есть одна идиома кода, которая бы сломалась, если бы INT были 64-битными, и я вижу ее достаточно часто, что я думаю, что это можно было бы назвать разумным:
- проверить, является ли значение отрицательным, тестируя, если
((val & 0x80000000) != 0)
Это обычно встречается при проверке кодов ошибок. Много стандартов кода ошибок (например, Window HRESULT
) использует бит 31 для представления ошибки. И код иногда проверяет эту ошибку либо путем тестирования бита 31, либо иногда проверяя, является ли ошибка отрицательным номером.
Макросы Microsoft для тестирования HResult Используют оба метода - и я уверен, что существует тонна кода, который делает одинаковую без использования макросов SDK. Если бы MS перешел в ILP64, это была бы одна область, которая вызвала портирующую головную боль, которой полностью избегают модели LLP64 (или модели LP64).
ПРИМЕЧАНИЕ. Если вы не знакомы с такими терминами, как «ILP64», см. Мини-Глоссарий в конце ответа.
Я почти уверен, что существует много кода (не обязательно ориентированного на Windows), в котором используется обычный старик для хранения кодов ошибок, предполагая, что эти INT имеют 32-битный размер. И держу пари, что есть много кода с этой схемой состояния ошибки, которая также использует оба вида проверки (< 0
и бит 31 установлен) и который сломается, если перенесено на платформу ILP64. Эти проверки могут быть сделаны для того, чтобы продолжать работать правильно в любом случае, если коды ошибок были тщательно построены, чтобы провести вытяжение знака, но, опять же, многие такие системы, которые я видел, строили значения ошибки, или собрав кучу кучу. Анкет
В любом случае, я не думаю, что это неразрешимая проблема каким -либо образом, но я думаю, что это довольно распространенная практика кодирования, которая приведет к тому, что много кода потребует исправления, если перенесено на платформу ILP64.
Обратите внимание, что я также не думаю, что это была одна из главных причин для Microsoft выбрать модель LLP64 (я думаю, что это решение было в значительной степени обусловлено совместимостью бинарных данных между 32-битными и 64-битными процессами, как упоминается в MSDN а также В блоге Раймонда Чена).
Мини-Глоссарий Для 64-битной терминологии программирования платформы:
- ILP64:
int
,long
, указатели 64-бит - LP64:
long
и указатели 64 бита,int
32-битные (используются многими (большинство?) Unix платформами) - LLP64:
long long
и указатели 64 бита,int
а такжеlong
Оставаться 32-битным (используется на Win64)
Для получения дополнительной информации о 64-битных моделях программирования см. "64-битные модели программирования: почему LP64?"
Хотя я лично не пишу код, так, держу пари, что он существует в нескольких местах ... и, конечно, он сломается, если вы измените размер int
.
int i, x = getInput();
for (i = 0; i < 32; i++)
{
if (x & (1 << i))
{
//Do something
}
}
Ну, это не так, как эта история - все новая. С «большинством компьютеров» я предполагаю, что вы имеете в виду настольные компьютеры. Уже было переход от 16-битного до 32-битного int
. Анкет Есть ли что -то вообще, что говорит о том, что такая же прогресс не произойдет на этот раз?
Не особенно. INT составляет 64 бит на около 64 -битных архитектуры (не X64).
Стандарт на самом деле не гарантирует, что вы получите 32 -битные целые числа, только это (u) int32_t может удерживать один.
Теперь, если вы зависите от int, такой же размер, что и ptrdiff_t, вы можете быть сломаны.
Помните, C не гарантирует, что машина даже является бинарной машиной.