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を比較すると下記のことが言える。
- long型は4byte幅である
- 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を前提に書かれたコードを修正するならそのような修正になるのでは? とのこと。
この辺どうするかという話はありそうですが。