2019年3月1日金曜日

整理しよう整数型

いつのまにか 64ビットアプリが当たり前になり、Lazarus/Delphi でも 64ビットアプリを当たり前のように作成できるようになりました。
将来的には、Mac のように64ビットアプリいっぽんに統一されていくのでしょうが、Windows の 32ビット版が淘汰されるまでは相当時間がかかると思われますので(未だに Windows XP や IE が現役ですからねぇ)、今のところ我々エンジニアは、ソースを32ビットでも64ビットでも正しくコンパイルできるようにしておく必要があります。
その際に最も問題になるのは「整数型としてどの型を使うか」という点ではないでしょうか。JavaScript のようなスクリプト型の言語ですと、数値型には整数型と浮動小数点型の2つしかなかったりしますので悩まなくてもよいのですが(悩みたくても悩めないというのが正解でしょうか)、Lazarus/Delphi/C のようなコンパイル型の言語には、整数型がたくさんあるため、きちんと使い分ける必要があります。
そこで、整数型について整理して、おすすめのルールを作成してみました。

おすすめルール1

まず、32ビット整数の範囲で収まる変数については、次のように NativeInt、NativeUInt を使うのが最強でしょう。こうすることでコンパイラ生成コードが最適化されやすくなるはずです。

var i: NativeInt;
var u: NativeUInt;

もっとも、型名が長いので、次のように別名を定義するとよいでしょう。

type
  int = NativeInt; uint = NativeUInt;
  pint = ^int; puint = ^uint; // ついでにポインタ参照型も宣言しとく

var i: int;
var u: uint;

おすすめルール2

次に、32ビットを超える値を扱う変数については、NativeInt や LongInt のような「プラットフォーム依存の整数型」を使うのは避け、次のように Int64 型や Uint64 型を用いて、32ビットを超える値を扱う変数であることを明示的にするのがよいでしょう。

var i: int64;
var u: uint64;

おすすめルール3

さらに、ファイルに保存するための領域など、サイズを固定化すべきところでは、NativeInt や LongInt のような「プラットフォーム依存の整数型」を使うのは避けるべきでしょう。次の例は失敗例です。

var buf: longint;
stream.write(buf, sizeof(buf));

これではプラットフォームが違うと読み込めえないファイルが作成されてしまいます。
おそらくこの結果は多くの方が望むものではないはずですので次のようにすべきでしょう。

var buf: int32;
stream.write(buf, sizeof(buf));

なお、integer 型も「プラットフォーム依存の整数型」ではありませんので、integer 型を使ってもよいのですが、int32 型の方がパッと見で変数領域サイズが分かりやすいのでよいと思います。

おすすめルール番外編

「ルールが3つもあるの面倒!」という方は、「NativeInt や LongInt のようなプラットフォーム依存の整数型は絶対に使わない!」というルールだけでも十分です。ようは「プラットフォーム依存の整数型」とそれ以外の整数型をしっかり区別して使い分けできていればいいだけなのですから。

最後に

いかがでしたでしょうか。
「プラットフォーム依存の整数型」とそれ以外の整数型の区別について詳しくお知りになりたい方には http://docwiki.embarcadero.com/RADStudio/Tokyo/ja/%E5%8D%98%E7%B4%94%E5%9E%8B%EF%BC%88Delphi%EF%BC%89 が便利かと思います。