ポインターのアドレスを整数型にキャストするときは要注意

注意: この記事は1年以上前に掲載されたものです。情報が古い場合がありますのでお気を付け下さい。

C言語及びC++ではポインターと呼ばれる、そのメモリー領域のアドレスの場所を指し示す特有の機能があるが、それの計算等を行う際に往々にしてキャストが行われるが、その際に環境依存の問題を引き起こす危険性がある。これから述べたいのは、私が見たことのある例で、普通であれば絶対に問題を引き起こすものである。

今回、私が遭遇したものは、以下のようなコードのものである。

void *ptr = getptr();
unsigned int addr = (unsigned int)ptr;
something(addr);

上記のコードでは、ポインターの計算こそ行っていないものの、環境によってはバグに悩まされるコードの例である。

というのは、void *ptr = getptr();で取得したアドレスの値、及びそれを格納するサイズは環境によって変わるからである。例えば、一般的な32bit環境では32bit型として扱われるため、このままでも基本的には問題はない ((WindowsやUNIX系OS、GNU/Linuxシステムなど、一般的な32bitアーキテクチャーで採用されているILP32の場合は問題ない。LP32ではunsigned intは16bit型の為、バグが発生する)) のだが、64bit環境では多くの場合バグが発生する危険性が高い。

これは、64bit環境では、ポインターのアドレス値が64bitで扱われる一方、多くのシステムではint/unsigned int型が32bit型のままであることで、キャストの際に値の一部が消失してしまうことがあることが原因である。

これを防ぐには、適切な型に切り替える必要がある。C99以降及びC++11以降では以下のような書き方ができる。

void *ptr = getptr();
uintptr_t addr = (uintptr_t)ptr;
something(addr);

こうすることによって、ポインターのサイズが扱える値が使えるため、問題が発生することが基本的になくなる。万一比較的古い仕様のC言語を使う場合でも、適切に#ifdef〜#endif及びtypedefを駆使することでほぼ同様のことは行える。

C言語においてはこのように他の言語にはない落とし穴があるので、是非とも気をつけてほしい。

タイトルとURLをコピーしました