|
1.3 LP64プログラミング環境 |
|
64bitプロセサのためのプログラムを、C/C++で書く際に注意すべき点の1つが、整数データモデルの違いです。
32bit環境ではポインタは32bitであり、int型と同じ幅でした。64bitプロセサのプログラミング環境では、ポインタの幅が64bitとなります。Cの言語仕様(ISO/IEC
9899:1999 Programing Languages -
C[5]、C99)においては、intは「実行環境のアーキテクチャの暗示する自然な大きさ」となっており、64bitプロセサにおいては64bitとなりそうではありますが、そうではない実装が主流となっています。主な整数データモデルを【表1.2】に示します。
|
|
 |
ILP32 |
|
|
int、long int、ポインタが32bit、すなわち一般的な32bit環境です。long
long int(※1)として64bit整数が提供される処理系も多くなっています。
|
|
 |
LLP64 |
|
|
ポインタを除き、各整数型についてはILP32と同じデータモデルです。64bit版のWindows(Win64)ではこのデータモデルが採用されています。32bit環境で作られ、int型またはlong
int
型の変数にポインタを保持するような処理をしているプログラムをLLP64環境に移植しようとすると、ポインタの上位32bitが落とされて致命的な問題が発生することがあります。ポインタを除く整数型についてはILP32と同じなので、それ以外の問題は起きにくいと考えられます。
|
|
 |
LP64 |
|
|
long intを64bitとしたデータモデルです。Linuxも含めて、多くの64bit
Unixではこのデータモデルが採用されています。int型変数にポインタを保持するようなプログラムでは、LLP64と同じような問題が発生しますが、long
int型変数の場合には問題が顕在化しません。また、32bit環境においては、intとlong
intが混用されても問題にならないことが多く(※2)、こうしたプログラムもLP64環境に移植した際に問題が発生します。
|
|
 |
ILP64 |
|
|
intも64bitとしたデータモデルです。一部のオペレーティングシステムやコンパイラがそのような動作モードを持ちますが、LP64など他のデータモデルも用意されていることが多く、使われることは少ないようです。int型やlong
int型の変数にポインタを格納しても、情報の欠落は発生せず、問題が顕在化しません。
|
|
|
処理系によっては、複数のデータモデルをサポートしており、コンパイラのオプションなどで切り替えることができます。たとえば、x86-64用のLinuxで広く使われているC/C++コンパイラgccでは、-m64オプションを付けることでLP64モード、-m32オプションを付けることでILP32モードとなります。-m32オプションを付けた場合、出力されるバイナリファイルはIA-32用になります。
C99では、整数型の幅を明示的に指定するための方法を規定しています。ヘッダファイル<stdint.h>を#includeすることで、
以下の型が適切にtypedefされます。
|
|
 |
intN_t |
|
|
ちょうどNbitの幅を持つ符号付き整数の型。一般に、N
は8、16、32、64。
|
|
 |
uintN_t |
|
|
ちょうどNbitの幅を持つ符号なし整数の型。一般に、N
は8、16、32、64。
|
|
 |
intmax_t |
|
|
もっとも幅の広い符号付き整数の型。
|
|
 |
intptr_t |
|
|
ポインタを保持できる符号付き整数の型。
|
|
 |
uintptr_t |
|
|
ポインタを保持できる符号なし整数の型。
|
|
 |
int_leastN_t |
|
|
最低Nbitの幅を持つ符号付き整数の型。N
は8、16、32、64。
|
|
 |
uint_leastN_t |
|
|
最低Nbit の幅を持つ符号なし整数の型。N
は8、16、32、64。
|
|
 |
int_fastN_t |
|
|
最低Nbitの幅を持ち、かつ最も処理が速い符号付き整数の型。N
は8、16、32、64。
|
|
 |
uint_fastN_t |
|
|
最低Nbitの幅を持ち、かつ最も処理が速い符号なし整数の型。N
は8、16、32、64。
|
|
|
#include <stdio.h>
#include <stdint.h>
/* inttypes.h
使用時は自動的に読み込まれる*/
#include <inttypes.h>
int main(void)
{
uint8_t a = 30;
int64_t b = 10;
printf("a=%" PRId8 "(0x%" PRIx8 "), b=%" PRId64 "\n", a, a, b);
return 0;
} |
|
|
|
実行例 |
|
|
|
【図1.1: inttypes.h、stdint.h の使用例】 |
|
さらに、これらに対応してprintf関数などの書式文字列も定義されています。ヘッダファイル<inttypes.h>を#includeすることで、以下のマクロが適切に定義されます。
|
|
 |
PRIdN、PRIiN |
|
|
intN_tを10進数で出力する変換指定子。dまたはiに適切な長さ修飾子を付けたもの。
|
|
 |
PRIuN |
|
|
uintN_tを10進数で出力する変換指定子。uに適切な長さ修飾子を付けたもの。
|
|
 |
PRIoN |
|
|
uintN_tを8進数で出力する変換指定子。oに適切な長さ修飾子を付けたもの。
|
|
 |
PRIxN、PRIXN |
|
|
uintN_tを16進数で出力する変換指定子。xまたはXに適切な長さ修飾子を付けたもの。
|
|
|
同様に、PRIdMAX、PRIdPTR、PRIdLEASTN、PRIdFASTN
なども定義されます。さらに、scanf関数などのための書式文字列として、SCNdN
なども定義されます。
|
|
|
|
※1 |
C99で規定されましたが、処理系によってはそれ以前からサポートされていました。 |
|
※2 |
典型的には、long int型の変数を、printfの書式文字列%d、%x(長さ修飾子なし)などで表示しようとするものです。 |
|
|
|
|
|
|
戻る 次へ
 |