ポインタ整数説(仮)

「C ではポインタを適切な整数に変換して戻すと元のポインタが復活する」っていうお話。これ、結構よく聞く話だし、ずっとそうなんだと思ってたんだけれど、根拠を探したら見当たらなかった。C99 (JIS X 3010:2003) 曰く、6.3.2.3 節にて

整数は任意のポインタ型に型変換できる。これまでに規定されている場合を除き、結果は処理系定義とし、正しく境界調整されていないかもしれず、被参照型の実体を指していないかもしれず、トラップ表現であるかもしれない(56)。
任意のポインタ型は整数型に型変換できる。これまでに規定されている場合を除き、結果は処理系定義とする。結果が整数型で表現できなければ、その動作は未定義とする。結果は何らかの整数型の値の範囲に含まれているとは限らない。



(56) ポインタを整数に変換する関数、又は整数をポインタに変換する関数は、実行環境におけるアドレス構造に関して一貫した動作をするように作られている。

となっていて、これを見る限りだと

void *p = ...;
if (p == (void *)(int)p) ...

みたいなのは良くて処理系定義、悪ければ未定義のように思える。一応、標準の stdint.h ヘッダの中に intptr_t という型の定義がある場合には、その型は

- すべての正しい void へのポインタがこの型へ変換可能である。
- この型から void へのポインタに逆変換することが可能であり、その結果が元のポインタと比較して等しくなる。

という条件をともに満たすので(7.18 および 7.18.1.4 節)、上のプログラムで int の代わりに intptr_t を使えば全く問題ない。ただ、規定によれば、処理系はこの型を必ずしも提供しなくて良いそうだ。

C99 になってこの辺の仕様が変わったのかとも思って、昔々の K&R(日本語、第2版)を見てみたら、A6.6 節に

ポインタはそれを保持するのに十分大きい整数型に変換してよい。必要とされるサイズは処理系によって異なる。そのマッピング関数も処理系依存である。
整数型のオブジェクトはポインタに明示的に変換できる。そのマッピングはポインタから同じポインタに変換し戻すのに十分大きな整数を介して常に行われるが、その他の点では処理系に左右される。

と書いてあった。なんか暗にポインター・イズ・整数でっせと言っているような気もするが、厳密に言えば一方のマッピングが他方の逆写像になっているとは書いていないので、やはり処理系定義と読むべきだろう。