Table of Contents

Написання портативного C коду є кутовим елементом професійної інженерії програмного забезпечення, що дозволяє додаткам виконувати різні апаратні архітектури, операційні системи та компілятори з мінімальним ремекцією. Портованість знижує рівень обслуговування, розширює базу користувачів, а також майбутній код захисту від платформи, що розвиваються. Ця стаття розширює бойові практики для досягнення істинної переносності, заземленої в стандарті C та десятки реальних спільнот.

Розуміння переваг платформи

Перед застосуванням методів портабельності розробники повинні розпізнати види варіації, які існують між платформами. Ці відмінності охоплюють чотири категорії: компіляторна поведінка, операційні API, апаратна архітектура, ресурсні обмеження.

Сортування за компілятором

C компілятори – від GCC, Clang та MSVC до вбудованих інструментів, таких як IAR та Keil – реалізовувати стандарт C з різним рівнем конформансу. Вони можуть відрізнятися в їх обробці підписності, макетування бітумів, структурна начинка, а також точні семантика або . Розширення мови (наприклад, розширення GNU C, розширення Microsoft ) може також створювати приховані залежності.

Диференції операційної системи

POSIX-подібні системи (Linux, macOS, BSD) діляться багатьма API, але Windows видає принципово різні множини системних дзвінків. Файл I/O, різьблення, динамічне зв'язування, сигнали та контроль процесу часто вимагають або умовної компіляції або абстрагованого шару. Навіть файлова назва випадку чутливість і шлях сепаратори (backslash проти. вперед зіткнення) вимагають догляду.

Архітектура та витривалість

Процесори відрізняються за розміром слово (32‐bit проти 64‐bit), байтовим замовленням (велике ендіан або мало‐ендіан), вимогами вирівнювання та інклюзивними функціями. Код, який приймає , становить 32 біти або що тостер підходить в ], не буде на багатьох платформах. Ендіанність стає критичним при послідовних даних для мережевого носіння або зберігання файлів.

Ресурсні обмеження

Вбудовані системи або глибокі вбудовані цілі можуть не мати операційної системи, мають обмежені розміри стека/пи, а також забезпечити виконання з обмеженими типами форматів. Портативний код повинен уникати припущення щодо наявності пам'яті та підтримки часу.

Основні кращі практики для портативного коду

Поруч із стандартними циліндрами C

Бібліотека C стандартної бібліотеки (ISO/IEC 9899) забезпечує базову базу, яка повинна поставлятися кожен конформуючий компілятор. Функції, такі як , , , і , які однаково мають на платформах. Уникайте платформи специфічні еквіваленти, такі як (POSIX), якщо охороняється ]. Для математичних операцій, відда перевагу ] над специфікаційними векторними бібліотеками.

Використання Фіксованих-Width Integer Types

головки визначає типи, такі як , , і , які гарантують точний розмір. Завжди використовуйте їх, коли діапазон значень – наприклад, коли розшифровування протоколів буферів або апаратних реєстрів. Аналогічно, використання формати specifiers (, ) для друку цих типів портально.

#include <stdint.h>
#include <inttypes.h>
int32_t val = -100;
printf("Value: %" PRId32 "\n", val);

Уникайте припущення щодо основних типів

Ніколи не припустимо, що є 32 біти, є 64 біти, або що підписано. Використовуйте і константи (], ]) для деревих властивостей в компіляційному часі. Для токерів, використання або , якщо ви повинні зберігати їх як ціле.

Рука Endianness Експлуатована

При обміні бінарних даних по машинах (мережа, файл або загальна пам'ять), завжди перетворюються на відомий байтовий замовлення – звичайно мережевий байтовий замовлення (великомендіан). Функції POSIX , , , ] доступні, для не‐POSIX систем, забезпечують власні впровадження за допомогою і виявлення часу.

Реквізити файлової системи

Файл шлях delimiters відрізняється (] на Unix, на Windows). Використовуйте макроси або невелику утиліту функцію, яка нормалізує шляхи. Для герметизації каталогу POSIX API стандарт; на Windows ви можете обгорнути за таким же інтерфейсом. Уникайте важкого покриття абсолютної доріжки.

Мінімізний невизнаний і реалізований Behaviour

Стандарт C створює багато операцій, як не визначені або реалізовані. Приклади включають в себе підписаний ціле перекриття, перемикання більш ніж ширина типу, а також оцінку . Використовуйте статичні аналізатори, такі як Cppcheck] або Clang‐Tidy, щоб зловити такі візерунки, і записувати код, який суворо відповідає.

Leverage Preprocessor Macros для вибору Compile‐Time

Кондиціональна компіляція є важливим для платформи-специфічний код, але неправильне використання може створитиплутані мези. Використовуйте добре відомі заздалегідь визначені макроси: , , ], , і компілятор макроси, як . Завжди документ кожен відділення і зберегти платформи конкретні розділи невеликими.

#ifdef _WIN32
 #include <windows.h>
 #define SLEEP(ms) Sleep(ms)
#else
 #include <unistd.h>
 #define SLEEP(ms) usleep((ms)*1000)
#endif

Використання шарів абстракції для системних дзвінків

Для різьблення, розетки, таймери та управління пам'яттю, створення тонких обгортань. Наприклад, визначити тип і функції, які карти на POSIX нитки на Unix і на Windows. Те ж саме підхід працює для динамічних бібліотек ( проти ). Багато відкритих бібліотек (наприклад, ]plibc], Apache APR) вже забезпечують такі анотації.

Тест на кілька платформ Рано і часто

Безперервна інтеграція (CI) трубопроводів повинні компілювати і запустити тестовий пакет на Linux, macOS, Windows, і будь-який вбудований цільовий. Використовуйте крос-компіляри та емулятори (наприклад, QEMU) для зловживання архітектурно-оптичних помилок перед розгортанням. Автоматичне тестування з інструментами, такими як / ctest / CCTest допомагає виконувати переносимість.

Методики підвищення переносності

Створення конфігурації системи з CMake або Autotools

Сучасні системи побудови можуть визначати характеристики платформ настройок часу. , , і модулі генерують , що ваш код може включати. Це замінює ламки ланцюжки з єдиною точкою правди.

// Generated config.h
#define HAVE_STDINT_H 1
#define WORDS_BIGENDIAN 0
#define SIZEOF_LONG 8

Портативна інлайн Асамблея та інтриники

Коли продуктивність вимагає платформи-специфічної інструкції (наприклад, SIMD, CPUID), інкапсулювати їх в окремих файлах і вибрати правильний файл під час створення. Використовуйте компіляторську інтризивну (наприклад, від GCC / Clang / ICC / VS), а не вбудовану збірку, оскільки інтриники набагато більше переносні через компілятори на тій же архітектурі.

Конструкції з питань сигналізації даних, які визначаються

Структура упаковки та вирівнювання змін. Використовуйте та specifiers від C11 (]) для виконання вирівнювання. Для старших компіляторів використовуйте попередньо оброблені робочі середовища ( для GCC, для MSVC).

Signal Handling сумісність

Signal константи (, ) і безпечне управління сигналами відрізняється широко. POSIX API бажано старшим . На Windows сигнали емульгуються через приставки управління консольами. Рефератно-сигнальна реєстрація за загальною функцією, щоб уникнути сюрпризів.

Реальні світові Пітуси та як уникнути

Без Падбека

є стандартом на POSIX але відсутні на багатьох вбудованих платформах і старих середовищах Windows. Використовуйте переносну реалізацію, як plibc або встановіть мінімальну ] під допустимою лікацією.

- Вхідний інтегер

C стандарт тільки говорить - це реальний тип, здатний представляти час. На деяких вбудованих системах він не відзначає значення 32‐bit; на інших мовах він є 64-розрядним підписаним цілим. Ніколи не виконує арифметичне значення ] без перевірки його властивостей, або використання для відмінностей.

Неглекційна нитка-Сафети в системах

Функції, такі як , , і , використання статичних буферів і не є різьбою. Використовуйте реактивні варіанти (], ]), де можливо, і забезпечити виконання скидання на платформах, які їх не вистачає.

Висновок

Написання портативного C коду є як дисципліною, так і інвестиції. Прилипаючи до C стандарту, вибираючи фіксовані типи абстракцій, абстракційні системні інтерфейси, і тестування по декількох платформах розробники можуть виробляти програмне забезпечення, яке працює надійно в середовищі, починаючи від надкомп'ютерів до мікроконтролерів. На практиці викладені тут – поєднані з сучасними конфігураціями системи та статичним аналізом – утворюють міцний фундамент для кросплатформного C розробки. Пам'ятайте: Портабельність не є післясумком; це завдання дизайну, яка сплачує дивідендів протягом усього життєвого циклу проекту.