Table of Contents
ポータブルCコードを書くことは、専門的なソフトウェアエンジニアリングの礎であり、アプリケーションが多様なハードウェアアーキテクチャ、オペレーティングシステム、コンパイラを横断し、最小限の作業を可能とします。 ポータビリティは、メンテナンスのオーバーヘッドを削減し、ユーザーベースを拡張し、進化するプラットフォームに対する将来の防止コードを拡張します。 この記事では、実際のポータビリティを達成するための戦いテストされたベストプラクティスを蒸留し、C規格と実際の経験の10年を基盤としています。
プラットフォームの差分を理解する
移植性技術を適用する前に、開発者はプラットフォーム間で存在するバリエーションの種類を認識しなければなりません。これらの違いは、コンパイラの動作、オペレーティングシステムのAPI、ハードウェアアーキテクチャ、リソースの制約の4つのカテゴリに分類されます。
作曲家のバリエーション
Cコンパイラ - GCC、Clang、MSVC から IAR や Keil などの組み込みツールへ – C 規格を異なるレベルの適合性で実装します。 の署名、ビットフィールドレイアウト、構造のパディング、および ] または []]] の処理に異なる場合があります。言語拡張(例えば、GNU C 拡張、Microsoft の :3:] も隠すことができます)。
オペレーティングシステムの差分
POSIX のようなシステム(Linux、macOS、BSD)は多くの API を共有していますが、Windows はシステムコールの根本的に異なるセットを割っています。ファイル I/O、スレッド、動的リンク、シグナル、プロセス制御は、条件付きコンパイルまたは抽象的なレイヤーを要求します。ファイル名ケースの感度とパスセパレータ(backslash 対. フォワードスラッシュ)でも、注意が必要です。
ハードウェアアーキテクチャとエンディアンネス
プロセッサは、単語サイズ(32〜ビットと64〜ビット)、バイトオーダー(ビッグ‐エンドまたはリトルエンド)、アライメント要件、および命令セット機能の違いです。 [を想定するコードは、32ビットまたはでポインタが収まると、多くのプラットフォームで失敗します。 ネットワークまたはファイルストレージのデータをシリアライズするとき、エンドアンネスは重要になります。
資源制約
組み込みシステムや深く埋め込まれたターゲットは、オペレーティングシステムが不足している、スタック/ヒープサイズが制限され、制限されたフォーマットのスペクシファイアーでの実装を提供する可能性があります。 ポータブルコードは、メモリの可用性とランタイムのサポートについて仮定しないようにする必要があります。
ポータブルCコードのコアベストプラクティス
標準的なCの図書館に頼って下さい
C標準ライブラリ(ISO/IEC 9899)は、コンパイラが供給しなければならないベースラインを提供します。 ]、 、 、 []]などの機能がプラットフォーム間で同一に動作します。 (POSIX)]などのプラットフォーム固有の同等物を避けてください。 によってガードされていない場合。 数学操作の場合、 :13]を好む ベクトルライブラリ上のベクトルライブラリ]。
固定式・幅整数式整数式
[ヘッダーは、、、およびのようなタイプを定義します。 常に値の範囲が重要であるとき、例えば、プロトコルバッファまたはハードウェアレジスタを定義するときに使用します。 同様に、]フォーマットの指定子()、これらのタイプを移植可能に印刷するために使用します。
#include <stdint.h>
#include <inttypes.h>
int32_t val = -100;
printf("Value: %" PRId32 "\n", val);
基本型について想定しないようにする
[は32ビットで、は64ビット、または]が署名されていることを仮定しないでください。 と定数([]]]])、 [[]]])を使用して、コンパイル時に派生するプロパティを有効にします。 スポイトのために、 またはを、またはを格納する必要がある場合は、それらが整数で保存する必要があります。
ハンドルのEndianness Explicitly
マシン(ネットワーク、ファイル、共有メモリ)を横断したバイナリデータを交換する際に、常に既定のバイト順(大文字)に変換します。 POSIX 関数 、、]、]] は、広く利用できます。 POSIX システムでは、独自の実装を ] に提供し、ランタイム検出を実行します。
抽象的なファイルシステム操作
ファイルのパス区切り文字は、Unix の で異なります。マクロや小さなユーティリティ関数を使用して、パスを正規化します。ディレクトリの反復では、POSIX ] API は標準です。Windows では、同じインターフェイスの後ろにある をラップできます。ハードコーディングの絶対パスを避けてください。
未定義・実装の最小化‐防御行動
C規格は、未定義または実装定義として多くの操作を設計しています。例えば、署名された整数オーバーフロー、タイプの幅を超えてシフトし、を評価する。このようなパターンをキャッチし、厳密に一致しているコードを書くには、]]Cppcheckまたは[[Clang‐Tidy]]のような静的解析器を使用します。
レバーのプリプロセッサマクロをコンパイル時の選択
条件付きコンパイルはプラットフォーム固有のコードで必須ですが、誤用は、具体的なメッセージを作成できます。よく知られている定義されたマクロ: ]、、、]、]]]、]]]などのコンパイラマクロを使用します。各のブランチを常に文書化し、プラットフォーム固有のセクションを小さく保ちます。
#ifdef _WIN32
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#else
#include <unistd.h>
#define SLEEP(ms) usleep((ms)*1000)
#endif
システムコールの抽象的なレイヤーを使用する
スレッド、ソケット、タイマー、メモリ管理のために、薄いラッパーを作成します。例えば、タイプとの定義は、UnixとのPOSIXスレッドにマップする機能です。同じアプローチは、動的ライブラリ()と)の対で動作します。多くのオープン ソースライブラリ(例えば、:[FLT:]:[FLT:]:[FLT:]:[FLT:]]:[F]]:[FLT:]]:[F]:[F]:[F]]]:[F]:]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[FLT:]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]:[F]
初期および多くの場合、複数のプラットフォームでテスト
連続統合(CI)パイプラインは、Linux、macOS、Windows、および任意の組み込みターゲットのテストスイートをコンパイルし、実行する必要があります。 デプロイ前にアーキテクチャ固有のバグをキャッチするために、クロス・コンパイラとエミュレータ(例えば、QEMU)を使用します。 ]]ctest]または]CMake/CTestなどのツールで自動化されたテストは、移植性を強化するのに役立ちます。
高度なポータビリティテクニック
CMakeまたはAutotoolsによるシステム構成の構築
現代のビルドシステムは、プラットフォーム特性を構成時に検出することができます。 CMakeの、、および[]モジュールは、コードが含めることができる[]を生成します。 これは、単一のポイントでブリル [チェーンを置き換えます。
// Generated config.h
#define HAVE_STDINT_H 1
#define WORDS_BIGENDIAN 0
#define SIZEOF_LONG 8
ポータブルインラインアセンブリとインテリスティックス
パフォーマンス要求のプラットフォーム固有の命令(例えば、SIMD、CPUID)が、それらを別々のファイルでカプセル化し、ビルド中に適切なファイルを選択します。 同じアーキテクチャのコンパイラのイントリニシックス(GCC/Clang/ICC/VSからのような)を使用して、インラインアセンブリではなく、イントリニシックスは、同じアーキテクチャ上のコンパイラ全体でよりポータブルであるため、。
データを一元化し、データ構造を明示的に検証
構造の梱包とアライメントは異なります。 と []] の整列を強制するために C11 (]) のスペクチャを使用します。 古いコンパイラでは、GCC の ] のプリプロセッサーベースの回避策(])を使用します。 ]])。
信号処理の両立性
信号定数(])、)、および安全信号処理は広く異なります。 POSIX APIは、古いに好ましいです。 Windowsでは、信号はコンソール制御ハンドラを介してエミュレートされます。 驚きを避けるために、一般的な機能の背後にあるアブストラクト信号登録。
実質的‐世界的ピッタフォールとテムを回避する方法
に頼る フォールバックなし
は、POSIX では標準ですが、多くの組み込みプラットフォームと古い Windows 環境で欠落しています。 のようなポータブル実装を使用して、 許認可の下で を最小限に束ねます。
を標識した整数
C 規格は、時刻を表すことができる実型である のみを言います。 いくつかの組込みシステムでは、符号なしの 32 ビット値です。 他の人では 64 ビット署名整数です。 プロパティをチェックせずに で算数を実行しないでください。 違いは ] を使用します。
システムコールにおけるスレッドの安全性の無視
[、、および[]のような関数は、静的バッファを使用し、スレッドセーフではありません。 再エントラントバリアント(]])、 [)を使用して、それらが欠けているプラットフォームでフォールバックの実装を提供します。
コンテンツ
ポータブルCコードを書くことは、両方の規律と投資です。 C規格に密接に固執することにより、固定幅タイプ、抽象的なシステムインターフェイス、複数のプラットフォーム間でのテストを選択することで、開発者は、スーパーコンピュータからマイクロコントローラまで、確実に実行するソフトウェアを生成できます。 ここに概説された慣行は、現代のビルドシステム構成と静的分析と組み合わせて、クロスプラットフォームC開発のための耐久性のある基盤を形成します。 覚えておいてください:移植性は、後方ではない; それは、プロジェクト全体のライフサイクルの目標を支払い、設計です。