Будет ли простой int 64-бит сломать много разумного кода?
До недавнего времени я считал, что большинство разработчиков/поставщиков систем решили, что простая 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
".
Ответы
Ответ 1
Как вы говорите, я считаю, что правила продвижения действительно являются убийцей. uint32_t
затем продвигается до int
, и вы внезапно подписали арифметику, где почти все ожидали без знака.
Это было бы в основном скрыто в местах, где вы просто выполняете арифметику и назначаете обратно uint32_t
. Но это может быть смертельно опасно в тех местах, где вы сравниваете с константами. Является ли код, который полагается на такие сравнения без явного приведения, является разумным, я не знаю. Константы кастинга, такие как (uint32_t)1
, могут стать довольно утомительными. Я лично, по крайней мере, всегда использую суффикс U
для констант, которые я хочу быть неподписанными, но это уже не так читаемо, как хотелось бы.
Также имейте в виду, что uint32_t
и т.д. не гарантируется. Даже не uint8_t
. Применением этого является расширение от POSIX. Таким образом, в этом смысле C как язык далеко не способен сделать этот шаг.
Ответ 2
"Разумный код"...
Ну... дело в развитии, вы пишите и исправляете его, а затем оно работает... и затем вы останавливаетесь!
И, возможно, вас много сожгли, поэтому вы остаетесь в безопасных диапазонах определенных функций, и, возможно, вы не были сожжены таким образом, чтобы вы не понимали, что вы полагаетесь на то, что может иметь вид изменений.
Или даже если вы полагаетесь на ошибку.
В старых компиляторах Mac 68000 int был 16-битным и длинным 32. Но даже тогда большинство существующих C-кода предполагалось, что int было 32, поэтому типичный код, который вы нашли в группе новостей, не будет работать. (О, и у Mac не было printf, но я отвлекаюсь.)
Итак, что я получаю, да, если вы что-то измените, тогда некоторые вещи сломаются.
Ответ 3
С современными типами C99 с фиксированным размером (int32_t и uint32_t и т.д.) необходимость для того, чтобы быть стандартным целым числом тип каждого размера 8, 16, 32 и 64 в основном исчезает,
C99 имеет тип typeDEF с фиксированным размером, а не типы фиксированного размера. Нативные C-целые типы все еще char
, short
, int
, long
и long long
. Они по-прежнему актуальны.
Проблема с ILP64 заключается в том, что она имеет большое несоответствие между типами C и typedefs C99.
- int8_t = char
- int16_t = short
- int32_t = нестандартный тип
- int64_t = int, long или long long
От 64-битные модели программирования: почему LP64?:
К сожалению, модель ILP64 не обеспечивают естественный способ описания 32-битные типы данных и должны прибегать к не переносные конструкции, такие как __int32
для описания таких типов. Это, вероятно, вызовет практические проблемы в создании кода, который может работать как 32, так и 64-разрядные платформы без #ifdef
конструкций. Было возможно доставить большое количество код для моделей LP64 без необходимости внести такие изменения, в то время как поддержание инвестиций, сделанных в наборов данных, даже в тех случаях, когда информация о наборе не была сделана внешне видимый приложением.
Ответ 4
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-битными целыми числами (по крайней мере, квази-законными). Однако большинство кода не написано с использованием этих типов, и в коде есть типично глубоко укоренившиеся и в значительной степени скрытые (но в корне ошибочные) предположения, которые будут расстроены, если модель отходит от существующих вариантов.
Обратите внимание на сверхконсервативную модель LLP64 от Microsoft для 64-битной Windows. Это было выбрано потому, что слишком большой старый код сломался, если была изменена 32-битная модель. Однако код, который был перенесен на архитектуры ILP64 или LP64, не был сразу переносимым в LLP64 из-за различий. Теоретики заговора, вероятно, скажут, что были намеренно выбраны, чтобы сделать его более сложным для кода, написанного для 64-разрядной Unix, для портирования на 64-битную Windows. На практике я сомневаюсь, было ли это более чем счастливым (для Microsoft) побочным эффектом; 32-разрядный код Windows пришлось пересмотреть, чтобы использовать модель LP64.
Ответ 5
Там одна идиома кода, которая сломалась бы, если ints были 64-битными, и я вижу это достаточно часто, и я думаю, что это можно было бы назвать разумным:
- проверка отрицательного значения, если
((val & 0x80000000) != 0)
Это обычно обнаруживается при проверке кодов ошибок. Многие стандарты кода ошибки (например, Window HRESULT
) используют бит 31 для представления ошибки. И код иногда проверяет эту ошибку либо путем тестирования бит 31, либо иногда путем проверки того, является ли ошибка отрицательным числом.
Макросы Microsoft для тестирования HRESULT используют оба метода - и я уверен, что там есть тонна кода, которая аналогична, не используя макросы SDK. Если MS переместилась в ILP64, это была бы одна область, которая вызывала головные боли портирования, которых полностью избегали с помощью модели LLP64 (или модели LP64).
Примечание. Если вы не знакомы с такими терминами, как "ILP64", см. мини-глоссарий в конце ответа.
Я уверен, что там много кода (не обязательно ориентированного на Windows), который использует plain-old-int для хранения кодов ошибок, предполагая, что эти int 32-битные. И я уверен, что существует много кода с этой схемой состояния ошибки, в которой также используются оба вида проверок (< 0
и бит 31, которые установлены), и которые будут разбиты, если они будут перенесены на платформу ILP64. Эти проверки могут быть выполнены, чтобы продолжать работать правильно в любом случае, если коды ошибок были тщательно сконструированы таким образом, чтобы происходило расширение знака, но опять же, многие такие системы, которые я видел, создают значения ошибок путем объединения совокупности бит полей.
В любом случае, я не думаю, что это неразрешимая проблема каким-либо образом, но я думаю, что это довольно распространенная практика кодирования, которая заставит много кода требовать исправления, если переместится на платформу ILP64.
Обратите внимание, что я также не думаю, что это была одна из главных причин, по которой Microsoft выбрала модель LLP64 (я думаю, что это решение во многом было обусловлено совместимостью двоичных данных между 32-битными и 64-битными процессами, поскольку упомянутые в MSDN и в блоге Raymond Chen).
Мини-глоссарий для 64-битной терминологии модели программирования платформы:
- ILP64:
int
, long
, указатели - 64-разрядные
- LP64:
long
и указатели 64-битные, int
- 32-битные (используются многими (большинство?) Unix-платформ)
- LLP64:
long long
и указатели 64-битные, int
и long
остаются 32-битными (используются на Win64)
Для получения дополнительной информации о 64-битных моделях программирования см. 64-битные модели программирования: почему LP64?
Ответ 6
Ну, это не так, как эта история - все новое. С "большинством компьютеров" я предполагаю, что вы имеете в виду настольные компьютеры. Уже начался переход с 16-битного на 32-разрядный int
. Есть ли что-нибудь вообще, что говорит, что такое же прогрессирование не произойдет на этот раз?
Ответ 7
Пока я лично не пишу такой код, я буду держать пари, что он там в нескольких местах... и, конечно, он сломается, если вы измените размер int
.
int i, x = getInput();
for (i = 0; i < 32; i++)
{
if (x & (1 << i))
{
//Do something
}
}
Ответ 8
Не особенно. int 64 бит на некоторых 64-битных архитектурах (не x64).
Стандарт фактически не гарантирует, что вы получите 32-битные целые числа, просто (u) int32_t может содержать один.
Теперь, если вы в зависимости от int того же размера, что и ptrdiff_t, вы можете быть сломаны.
Помните, что C не гарантирует, что машина даже является двоичной машиной.