Будет ли простое int 64-битное нарушить много разумного кода? - PullRequest
15 голосов
/ 30 декабря 2010

До недавнего времени я считал, что решение большинства разработчиков и поставщиков систем сохранять простые 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".

Ответы [ 8 ]

6 голосов
/ 30 декабря 2010

Как вы говорите, я думаю, что правила продвижения действительно убийственные. uint32_t затем повысится до int, и вы вдруг станете подписывать арифметику, где почти все ожидают неподписанного.

Это будет в основном скрыто в тех местах, где вы просто делаете арифметику и присваиваете обратно uint32_t. Но это может быть смертельно в тех местах, где вы делаете сравнение с константами. Является ли разумным код, основанный на таких сравнениях без явного приведения, я не знаю. Использование констант типа (uint32_t)1 может стать довольно утомительным. Лично я по крайней мере всегда использую суффикс U для констант, которые я хочу подписать, но это уже не так читаемо, как хотелось бы.

Также имейте в виду, что uint32_t и т. Д. Не гарантируется. Даже uint8_t. Внедрение этого является расширением от POSIX. Так что в этом смысле C как язык далек от того, чтобы сделать это.

5 голосов
/ 30 декабря 2010

«Разумный код» ...

Ну ... главное в разработке, вы пишете и исправляете ее, а потом она работает ... и затем вы останавливаетесь!

Иможет быть, вы много обгорели, так что вы остаетесь в безопасном диапазоне определенных функций, и, возможно, вы не были обожжены именно таким образом , поэтому вы не понимаете, что полагаетесь начто-то, что может как-то измениться.

Или даже то, что вы полагаетесь на ошибку.

На старых компиляторах Mac 68000 int был 16-битным, а long - 32. Но даже тогда большинствоВ существующем C-коде предполагалось, что int равен 32, поэтому типичный код, который вы нашли в группе новостей, не будет работать.(О, и у Mac не было printf, но я отвлекся.)

Итак, что я имею в виду, да, если вы измените что-нибудь , тогда некоторые вещи сломаются.

3 голосов
/ 01 января 2011

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 LLP64 для 64-битной Windows.Это было выбрано потому, что слишком большой старый код сломался бы при изменении 32-битной модели.Однако код, который был перенесен на архитектуры ILP64 или LP64, не сразу переносился на LLP64 из-за различий.Теоретики заговора, вероятно, скажут, что он был намеренно выбран для того, чтобы затруднить перенос кода, написанного для 64-битного Unix, на 64-битную Windows.На практике я сомневаюсь, было ли это более чем счастливым (для Microsoft) побочным эффектом;32-битный код Windows пришлось много пересмотреть, чтобы использовать и модель LP64.

3 голосов
/ 30 декабря 2010

В современных типах C99 фиксированного размера (int32_t и uint32_t и т. Д.) Необходимость в наличии стандартного целочисленного типа каждого размера 8, 16, 32 и 64 в основном исчезает,

C99 имеет фиксированные размеры typeDEF, а не типы фиксированного размера.Собственные целочисленные типы C по-прежнему char, short, int, long и long long.Они по-прежнему актуальны.

Проблема с ILP64 заключается в том, что он имеет большое несоответствие между типами C и типами C99.

  • int8_t = char
  • int16_t = short
  • int32_t = нестандартного типа
  • int64_t = int, long или long long

From 64-битные модели программирования:Почему LP64? :

К сожалению, модель ILP64 не предоставляет естественного способа описания 32-битных типов данных и должна прибегать к непереносимым конструкциям, таким как __int32, для описаниятакие типы.Это может вызвать практические проблемы при создании кода, который может работать как на 32-, так и на 64-битных платформах без конструкций #ifdef.Было возможно портировать большие объемы кода на модели LP64 без необходимости вносить такие изменения, сохраняя при этом инвестиции в наборы данных, даже в тех случаях, когда информация о наборе не была видна приложению извне.

2 голосов
/ 31 декабря 2010

Есть одна идиома кода, которая сломалась бы, если бы целые числа были 64-битными, и я вижу это достаточно часто, так что я думаю, что это можно назвать разумным:

  • проверка, является ли значение отрицательным, проверяя, если((val & 0x80000000) != 0)

Обычно это встречается при проверке кодов ошибок.Многие стандарты кодов ошибок (например, HRESULT в Windows) используют бит 31 для представления ошибки.И код иногда проверяет эту ошибку, проверяя бит 31 или иногда проверяя, является ли ошибка отрицательным числом.

Макросы Microsoft для тестирования HRESULT используют оба метода - и я уверен, что есть тонна кодатам, что делает подобное без использования макросов SDK.Если бы MS перешла на ILP64, это было бы одной из областей, которая вызывала головные боли при портировании, которые полностью избегались при использовании модели LLP64 (или модели LP64).

Примечание: если вы не знакомы с терминамикак "ILP64", пожалуйста, посмотрите мини-глоссарий в конце ответа.

Я почти уверен, что есть много кода (не обязательно ориентированного на Windows), который использует простойold-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?»

1 голос
/ 30 декабря 2010

Хотя я лично не пишу подобный код, могу поспорить, что он существует более чем в одном месте ... и, конечно, он сломается, если вы измените размер int.

int i, x = getInput();
for (i = 0; i < 32; i++)
{
    if (x & (1 << i))
    {
        //Do something
    }
}
0 голосов
/ 30 декабря 2010

Не особенно. int является 64-битным на некоторых 64-битных архитектурах (не x64).

Стандарт на самом деле не гарантирует, что вы получите 32-битные целые числа, просто (u) int32_t может содержать одно.

Теперь, если вы зависите от int того же размера, что и ptrdiff_t, вы можете сломаться.

Помните, C не гарантирует, что машина является даже двоичной машиной.

0 голосов
/ 30 декабря 2010

Ну, эта история не нова. Под «большинством компьютеров» я предполагаю, что вы имеете в виду настольные компьютеры. Уже был переход с 16-битного на 32-битный int. Есть ли что-нибудь, что говорит, что на этот раз такого же прогресса не произойдет?

...