64bit環境とPHP
先週のPHP勉強会の僕のプレゼン「64bit PHPの罠」に間違いがありました(参照:「第51回PHP勉強会@関東に参加してきました」)。全体の主張はそれほど変わりませんが、下記の通り訂正いたします。
- 誤:「PHPのinteger型はCのint型」「64bit環境ではPHPのinteger型は64bitになる」
- 正:「PHPのinteger型はCのlong型」「Unix OSの64bit環境ではPHPのinteger型は64bitになる」
補足:PHP7からは「PHPのinteger型は32bit環境ならint32_t、64bit環境ならint64_t」「64bit環境ではPHPのinteger型は必ず64bitになる」と変わりました。
PHPのinteger型はlong
次のように、PHPのinteger型は内部的に共用体_zvalue_valueのlvalというメンバになっています。
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value;
このように、lvalはlong型です。このあたりは何度も見ているはずなんですが、プレゼン資料を作る時点では頭から抜けていました。
Cコンパイラとデータモデル
誤りの2点目は、long型が64bit環境で常に64bitというわけではない、ということです。
C言語では整数型のサイズに関する規定はそれほどありません。サイズとしてshort <= int <= long <= long longという規定がC99にはありますが、それ以外は仕様として曖昧です。これは仕様上の縛りが性能面での制約とならないように、それぞれのアーキテクチャ上で自然なサイズを選択していいよ、という意図でしょう。
補足:タレコミ情報によれば、C99ではサイズに関してもう少し制約があるそうです(5.2.4.2.1 Sizesof integer types<limits.h> )。
●short:16bit以上
●int:16bit以上
●long:32bit以上
●long long:64bit以上
とはいえ各社フリーダムに実装するということもなく、現存する32bit環境ではint・long・ポインタの全てのサイズが32bitであるようなデータモデルが一般的です(ILP-32)。
ところで、64bit環境ではデータモデルが何種類かあります。大抵のUnix系の環境では、32bit環境とのポータビリティの観点から、intは32bit、long・ポインタが64bitというデータモデルを採用しています(LP-64)。一方、Microsoftの64bit環境のVC++はlong longとポインタが64bit、intとlongは32bitという環境を採用しています(LLP-64)。
下記はWikipediaの記事「64ビット」にある表を再構成したもので、データモデルごとの型とサイズの対応を示したものです。
データモデル | int | long | ポインタ | 環境の例 |
ILP-32 | 32 | 32 | 32 | 大抵の32bit環境 |
LLP-64 | 32 | 32 | 64 | MS VC++(64bit) |
LP-64 | 32 | 64 | 64 | Unix系OS(64bit) |
ILP-64 | 64 | 64 | 64 | あまり使われない |
僕は思い込みでILP-64環境を仮定して話を進めていましたが、あまり使われないようですね。