Преобразование 32-битного приложения в 64-битное приложение на C
В настоящее время я работаю над преобразованием 32-битного приложения в 64-битное приложение на C. Это приложение в настоящее время работает на архитектуре x86 (Windows, OSX, Unix, Linux). Поэтому, прежде чем приступить к написанию кода, я хотел знать, что мне нужно учитывать при конвертации приложения.
9 ответов
- Узнайте, кто это написал. Они идиот? Они с вами несколько лет назад? Вы можете задать им вопросы? Они знакомы с существованием нескольких платформ и систем? Знание мышления автора (ов) программы поможет вам понять проблемы, когда вы столкнетесь с ними.
- Запустите и запустите 64-битную машину / среду сборки.
- Замените long на int. Помните, что
LONG
не являетсяlong
, - замещать
(int)&x
бросает и печатает сintptr_t
а также(unsigned int)&x
сuintptr_t
- Аудит все, что зависит от приведения структур к
char*
сделать с ним арифметику указателей. - Регулярно ищите \<4\>, если вы предполагали
4 = sizeof(void*)
- Потерпи. Когда вы найдете проблему, посмотрите в другом месте, если такая же проблема существует, и оберните решение в макрос.
- Старайтесь не использовать
#ifdef RUN64
или что-нибудь подобное. Вы пожалеете об этом, если 128-битные платформы когда-нибудь войдут в моду. - Инкапсулируйте все ваши изменения с точки зрения некоторых централизованных макросов, которые скрывают различия в переносимости в других местах вашей программы.
- Используйте тестер покрытия, чтобы убедиться, что вы покрыли все (при необходимости)
РЕДАКТИРОВАТЬ добавлено uintptr_t
обратите внимание, как предложено в комментарии.
Одна потенциальная проблема, которая еще не упоминалась, заключается в том, что если ваше приложение читает или записывает двоичные данные с диска (например, читает массив структур, используя fread
), вам придется очень тщательно проверить и, возможно, получить два считывателя: один для устаревших файлов и один для 64-битных файлов. Или, если вы осторожны, чтобы использовать такие типы, как uint32_t
и так далее от <stdint.h>
Заголовочный файл, вы можете переопределить ваши структуры, чтобы быть побитовым. В любом случае, бинарный ввод / вывод - вещь, на которую стоит обратить внимание.
Это действительно зависит от приложения и от того, как оно было закодировано. Некоторый код можно просто перекомпилировать с помощью 64-битного компилятора, и он будет работать, но обычно это происходит только в том случае, если код был разработан с учетом переносимости.
Если в коде много предположений о размере нативных типов и указателей, если в нем много хаков для упаковки битов или он обращается к внешнему процессу, используя протокол, указанный в байтах, но используя некоторые предположения о размере нативных типов, то для получения чистой компиляции может потребоваться некоторая или большая работа.
Практически каждое предупреждение о приведении и компиляции - это красный флаг, который нужно проверить. Если код не был "предупреждающим" для начала, это также является признаком того, что может потребоваться много работы.
Если вы использовали правильные типы для ваших значений - например. size_t
, ptrdiff_t
, uintptr_t
Типы int фиксированного размера из stdint.h
где это уместно - и не указывать размеры значений жестко, ваш код должен работать "из коробки".
Два основных различия между 32-битным и 64-битным программированием на C - это sizeof(void*) и sizeof(long). Основная проблема, с которой вы столкнетесь, заключается в том, что большинство систем Unix используют стандарт I32LP64, который определяет длину до 64 бит, а Win64 использует стандарт IL32LLP64, который определяет длину до 32 бит. Если вам требуется поддержка кроссплатформенной компиляции, вы можете использовать набор архитектурных определений типов для 32-разрядных и 64-разрядных целых чисел, чтобы гарантировать, что весь код будет работать согласованно. Это предоставляется как часть stdint.h как часть стандарта C99. Если вы не используете компилятор C99, вам может потребоваться развернуть собственный эквивалент
Как отмечалось в другом месте, основными задачами преобразования будет код, который принимает sizeof(int) == sizeof(long) == sizeof(void*), код для поддержки данных, записанных на диск, и код для межплатформенного IPC.
Для хорошего обзора истории этого взгляните на эту статью из ACM Queue.
Ну, по сути, количество изменений довольно мало, но это все равно будет основной задачей, если приложение не будет тщательно написано, чтобы быть несколько портативным для начала.
Основное отличие состоит в том, что указатели имеют ширину 64 бита, но большинство других типов данных не изменяются. Int по-прежнему 32-битный, а long, вероятно, также 32-битный. Так что, если ваш код преобразуется между int и указателями, это сломается. Точно так же любая структура или аналог, который зависит от определенного смещения для члена, может сломаться, потому что другие члены теперь могут быть больше, и поэтому изменить смещение.
Конечно, ваш код никогда не должен полагаться на эти трюки, так что в идеальном мире это вообще не будет проблемой, и вы можете просто перекомпилировать, и все будет работать. Но вы, вероятно, не живете в идеальном мире...;)
Основная проблема, с которой вы сталкиваетесь при переходе на 64-битную версию, заключается в том, что размер указателей различен (64-битная, а не 32 - duh). Размер целых чисел и размер long также могут различаться в зависимости от платформы.
Почему это проблема? Ну, это не так, если только ваш код не предполагает, что sizeof(int) == sizeof(void*). Это может привести к неприятным ошибкам указателя.
Все о 64-битном для разработчиков:
Статьи о 64-битной разработке
Коллекция ссылок 64-битных ресурсов
Инструмент Viva64
Уже есть много хороших ответов.
Подумайте об использовании Gimpel Lint. Он может указать именно те типы конструкций, которые являются проблематичными. Если ваш опыт похож на мой, он также покажет вам множество ошибок в системе, не связанных с 32/64 битным портом.