Написание переносимой программы на C - что нужно учитывать? - PullRequest
14 голосов
/ 20 февраля 2010

Для проекта в университете мне нужно расширить существующее приложение C, которое в конечном итоге должно работать на широком спектре коммерческих и некоммерческих Unix-систем (FreeBSD, Solaris, AIX и т.

Какие вещи я должен учитывать, когда я хочу написать программу на C, которая является наиболее переносимой?

Ответы [ 7 ]

23 голосов
/ 20 февраля 2010

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

Сохранение кроссплатформенного тестирования на конец приведет к сбою.

Это в сторону

  • Целочисленные размеры могут варьироваться.
  • числа с плавающей точкой могут быть представлены по-разному.
  • целые числа могут иметь различный эндианизм.
  • Параметры компиляции могут отличаться.
  • имена файлов могут отличаться.
  • реализация битовых полей будет отличаться.

Как правило, рекомендуется установить уровень предупреждения компилятора как можно выше, чтобы увидеть, на что может пожаловаться компилятор.

7 голосов
/ 20 февраля 2010

Раньше я писал утилиты C, которые затем поддерживал бы на 16- и 64-битных архитектурах, включая 60-битные машины. Они включали как минимум три разновидности «порядкового номера», разные форматы с плавающей запятой, разные кодировки символов и разные операционные системы (хотя преобладал Unix).

  1. Оставайтесь как можно ближе к стандарту C, насколько это возможно. Для функций / библиотек, не являющихся частью стандарта, используйте настолько широко поддерживаемую базу кода, сколько сможете найти. Например, для работы в сети используйте интерфейс сокетов BSD с нулевым или минимальным использованием опций сокетов низкого уровня, внеполосной сигнализации и т. Д. Чтобы поддерживать большое количество разнородных платформ с минимальным персоналом, вам придется остаться с простыми ванильными функциями.
  2. Помните о том, что гарантировано стандартом, а также о типичном поведении реализации. Например, указатели не обязательно имеют одинаковый размер с целыми числами, а указатели на разные типы данных могут иметь разную длину. Если вы должны сделать предположения, зависящие от реализации, тщательно документируйте их. Lint, или --strict, или что-либо подобное в вашем наборе инструментов разработки, жизненно важно здесь.
  3. Заголовочные файлы - ваш друг. Используйте определенные макросы и константы. Используйте определения заголовков и #ifdef, чтобы помочь выделить те случаи, когда вам нужно охватить небольшое количество альтернатив.
  4. Не предполагайте, что текущая платформа использует символы EBCDIC и упакованные десятичные целые числа. Существует немало ASCII - также есть две дополняющие машины. : -)

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

4 голосов
/ 20 февраля 2010

Одна конкретная проблема, о которой вам может потребоваться быть в курсе (например, если ваши файлы данных должны работать на разных платформах), это endianness .

Числа представлены по-разному на двоичном уровне на разных архитектурах. Системы с прямым порядком байтов располагают порядок старшего значащего байта, а системы с младшим порядком байта располагают порядок младшего значащего байта первым.

Если вы записываете некоторые необработанные данные в файл с одним порядком байтов, а затем читаете этот файл обратно в систему с другим порядком байтов, у вас, очевидно, будут проблемы.

Вы должны быть в состоянии получить порядковый номер во время компиляции на большинстве систем с sys/param.h. Если вам нужно обнаружить его во время выполнения, одним из методов является использование объединения int и char, затем установите char в 1 и посмотрите, какое значение имеет int.

4 голосов
/ 20 февраля 2010
  1. Используйте как минимум два компилятора.
  2. Наличие системы непрерывной сборки, которая предпочтительно строится на различных целевых платформах.
  3. Если вам не нужно работать на очень низком уровне, попробуйте использовать библиотеку, которая обеспечивает абстракцию. Маловероятно, что вы не найдете сторонние библиотеки, которые обеспечивают хорошую абстракцию для того, что вам нужно. Например, для сети и связи есть ACE. Boost (например, файловая система) также портирован на несколько платформ. Это библиотеки C ++, но могут быть и другие библиотеки C (например, curl).
  4. Если вам нужно работать на низком уровне, имейте в виду, что платформы иногда ведут себя по-разному даже на таких вещах, как posix, где они должны работать одинаково. Вы можете взглянуть на исходный код библиотек выше.
2 голосов
/ 20 февраля 2010

Это очень длинный список. Лучше всего читать примеры. Например, источник для perl . Если вы посмотрите на источник perl, вы увидите гигантский процесс создания заголовочного файла, который решает около 50 проблем с платформой.

Читай и плачь, или одолжи.

1 голос
/ 20 февраля 2010

Постоянно обращайтесь к стандартам POSIX для любых функций библиотеки, которые вы используете. Части стандарта неоднозначны, и некоторые системы возвращают разные стили кодов ошибок. Это поможет вам упреждающе находить те, кому действительно трудно найти немного отличающиеся ошибки реализации.

1 голос
/ 20 февраля 2010

Список может быть длинным, но он не так длинен, как поддержка Windows и MSDOS. Что характерно для многих утилит.

Обычный метод состоит в том, чтобы отделить модули основного алгоритма от модулей, которые имеют дело с операционной системой - в основном стратегия многоуровневой абстракции.

Различить несколько разновидностей Unix довольно просто в сравнении. Либо придерживайтесь функций, использующих одни и те же имена RTL, либо посмотрите на соглашение большинства для поддерживаемых платформ и #ifdef в исключениях.

...