hnwの日記

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環境を仮定して話を進めていましたが、あまり使われないようですね。

まとめ

  • PHPのinteger型はCではlong型
    • UNIX系の64bit環境だと64bit
    • 64bit Windows環境だと32bitのままだと思われる(確認環境が無いため未確認)