long型のサイズの方がアテにならない

64bitプロセッサのビルドに使うコンパイラについて、

long型とint型のサイズってなんぼに設定できるのと聞かれて資料を調べてた。


で、こういうデータサイズにはいくつかのモデルがあるんですね。

その中で最も広く使われているのがLP64というモデルだそう。

intが4byte、long, long longとポインタ型が8byteというもの。

(L=longとP=pointerが64bit幅なのでLP64というらしい)

なんでlongとlong longが同じなんだよと思ってしまうけど。

ただ、WindowsではLLP64というモデルを採用しており、

これだとintとlongが4byte、long longとポインタ型が8byteとなる。

(LL=long longとP=pointerが64bit幅なのでLLP64というらしい)


intのデータサイズは環境により異なり、longの幅は4byteで堅いという印象があったのだが、

これは16bitのシステムと32bitのシステムの比較ではその通りである。

その昔、AVRマイコンでプログラムを書いていたときには、intとポインタ型は2byte、longは4byteだった。

このようなデータモデルをIP16L32(I=intとP=pointerが16bit幅, L=longが32bit幅)というらしい。

一方で典型的な32bitシステムでは intとlongとポインタ型がいずれも4byte、

これをILP32(I=intとL=longとP=pointerが全て32bit幅)というそう。


IP16L32とILP32を比較すると下記のことが言える。

  1. long型は4byte幅である
  2. int型とポインタ型のサイズは同じである

少し前まで僕はこの理解でやってたんですよね。

もう16bit環境でプログラムを書くことなんてないわけだけど。

しかしこの性質はLP64ではどちらも成り立たない。

LLP64では1.は成り立つが、2.は成り立たない。というので困ってしまった。


どうしてこんなことになってるのだろうと調べたのだが、

どうもUNIX系ではポインタ型をlong型にキャストするコードが多くあるらしい。

このためポインタ型のサイズとlong型のサイズが合っていると好都合だったのだという。

なんでそんな書き方を多用してたのかよくわからないんだけどね。

この結果、ポインタとは無関係にlongを使っていたところが全部巻き添えになることに。


一方のWindowsではこのような必要性はなかったのか、

データサイズの互換性を重視し、ポインタ型以外はILP32のままのサイズにした。

ポインタ型と同じ幅の整数型はlong long(ILP32でも同様)と書くことになる。

ポインタ型を整数型にキャストする処理があれば、各々手を入れる必要はあるが、

それ以外のプログラムへの影響は小さいと言える。


で、どうも今回使うコンパイラはILP64は選べないっぽいんですよね。

そうするとLP64か……となるのだが、ILP32という選択肢はあるみたい。

えっ!? 64bitのシステムなのにポインタ型含めて32bit幅って大丈夫なの?

この点は確かに欠点で、32bitアドレスで表記できないところにはアクセスできなくなる。

ただ、ソースコードの互換性という点ではもっともよい方法である。

とはいえ、ある部分はLP64、ある部分はILP32みたいな混在は難しいようである。


これらのモデルの差を吸収する記法としては、

uint64_t とか int32_t とか、明示的にデータサイズを付けた型を使うことが考えられる。

ILP32を前提に書かれたコードを修正するならそのような修正になるのでは? とのこと。

この辺どうするかという話はありそうですが。