2006年4月22日

Cのポインタを整数に変換する

Cのポインタを整数に変換したいときがあります。このとき問題になるのは、ポインタのサイズが int と同じとは限らないということです。たとえば、 x86_64 の 64ビットのバイナリでは sizeof(int) = 4, sizeof(void *) = 8 となります。ポインタと同じサイズの整数を使いたい場合は C99 で導入された stdint.h で提供される intptr_t または uintptr_t を使います。

 

stdint.h には他にも int32_t や int64_t など、サイズつきの整数の型も提供されています。たとえば、uint64_t を使うと、64ビットの符合なし整数を扱うことができます。

私の場合、C++ のプログラムで void * から直接 uint64_t にキャストしようとしてはまりました。次のプログラムを x86_32 の環境でコンパイルして実行すると 80000000 ではなく、 ffffffff80000000 が表示されます。

#include <iostream>
#include <iomanip>
#include <stdint.h>
using namespace std;

int main() {
    void *p = (void *)0x80000000;
    uint64_t x = reinterpret_cast<uint64_t>(p);
    cout << hex << x << endl;
    return 0;
}
% ./a.out
ffffffff80000000

これは void *p の 0x80000000 が負の整数 (-2147483648) として解釈され、 64ビットに拡張される際に上位ビットに 1 が挿入されるために発生します。

この場合、直接 uint64_t にキャストしようとするのではなく、いったん uintptr_t にキャストすると問題は解決します。x86_32 では uintptr_t にキャストすると符合なしの 32ビットの整数になります。32ビットの符合なし整数を 64ビットの符合なし整数の変数に代入する際にはキャストは必要ありません。符合なしであるため、 64ビットに拡張される際には上位ビットに 0 が挿入されます。

#include <iostream>
#include <iomanip>
#include <stdint.h>
using namespace std;

int main() {
    void *p = (void *)0x80000000;
    uint64_t x = reinterpret_cast<uintptr_t>(p);
    cout << hex << x << endl;
    return 0;
}
% ./a.out
80000000

まとめ

ポインタを整数に変換する方法を紹介しました。stdint.h ではポインタと同じサイズの整数の型 intptr_t, uintptr_t およびその他の便利な整数の型が提供されています。32ビットのポインタから 64ビットの符合なしの整数に変換する際は上位ビットに 1が入らないよう、一度 uintptr_t にキャストするなどして気をつける必要があります。