移植性に関する指針
移植の際に生じる問題を最小に押さえる、 というのはソフトウェア工学的に、良い習慣です。 移植時に問題の生じうる箇所を少なくするテクニックとしては:
- 整数型や浮動小数型は保証される最小範囲の型である と考えておきます。 また、 型のサイズが増えたとしても正しく動くようにアルゴリズムを設計しておきます。
- 浮動小数演算は、変数の保持できるサイズよりも 上の精度で実行されることがあります。 浮動小数演算の関係するアルゴリズムは、 例え計算精度がいくら上がっても正しく動作するように組みます。
- コンパイラによって並び替えが行われるかもしれない部分では、
副作用の起きる順番に依存したコードを書かないようにします。例えば:
a + b + c
は (a + b) + c, a + (b + c), (a + c) + b, (c + b) + a, として評価される可能性があります。括弧は演算子の結合をコントロールしますが、 評価の順序はコントロールしません。
特に関数の引数は、 左から右に評価されたり右から左に評価されたりと、 呼び出し規約に応じて評価順序が変化します。
結合的な演算子 + と * のオペランドが浮動小数点数の場合は、 この並び替えは行われません。
- バイトオーダー (CPUがビッグエンディアンなのかリトルエンディアンなのか) に依存したコードは避けましょう。
- ポインタや参照のサイズが特定の整数型と等しい と仮定したコードは避けましょう。
- もしどうしても変数サイズを決め打ちしたいならば、
assert を入れてそのことをチェックします。
assert(int.sizeof == (int*).sizeof);
32bit から 64bit への移植性
64bitプロセッサやOSがだんだん使われるようになってきました。 以下のことを念頭に置いておきましょう:
- 整数型は、 32bitでも64bit環境でも同じサイズのままです。
- ポインタやオブジェクトへの参照は、32から64bitコードに変わることで、 4byteから8byteへとサイズが増加します。
- アドレス空間を表現する符号なし整数型としては、 size_t を使います。 配列の添え字の型も size_t であるべきです。
- アドレス空間を表現する符号付き整数型としては、 ptrdiff_t を使います。 二つのポインタの間の差を表現する型は、 ptrdiff_t であるべきです。
- .length, .size, .sizeof, .offsetof, .alignof プロパティの型は size_t です。
エンディアン
エンディアンとは、複数バイトの型がどのような順番でメモリに格納されるかの順序のことです。 よくある順序は ビッグエンディアン と リトルエンディアン の二種類です。 コンパイラは、コンパイル先システムの種類に応じて、 バージョン識別子 BigEndian か LittleEndian をあらかじめ定義します。 x86 システムでは常にリトルエンディアンです。
エンディアンが問題になるのは、以下のような場面です:
- 異なるエンディアンで書かれた 外部リソース (ファイルなど) を扱うとき。
- long や double など複数バイトの型を、 1バイトずつ読み書きするとき。
OS特有のコード
システム特有のコードは、 特有の部分を別のモジュールへ分離して取り扱います。 そしてコンパイル時に、システムに応じて適切なモジュールをimportします。
細かい違いであれば、 システム特有モジュールに定義した定数を使って、 if 文 や static if 文 で分けて書くこともできます。