
■long 32bit=4byteではなくなったがintは32bitのまま
typedef unsigned short U2;
typedef unsigned long U4;//要変更
gcc LP64モードで long は64bit=8byteです。
U4の様な型定義はサイズが変わってしまいます。
よく間違えますがLP64モードでは
intは32bitのままで64bitではありません。
■構造体の中のlongで穴が開く
typedef struct {
U2 d1; //2byte
U2 d2; //2byte
//ここに穴が開く
long data ; //4byteではなくなった
} tag_t ;
longが4byteから8byteに変わるため
境界調整のために
long の前に穴が開くかもしれません。
■sizeofの書式は%d⇒%zu,%zx
//■3.%dではなくて%zu,%zx
printf("sizeof(int)=%d\n", sizeof(int));
printf("sizeof(tag_t)=%d\n",sizeof(tag_t));
printf("sizeof(U4)=%d\n", sizeof(U4));
size_t len = sizeof(void *);
printf("sizeof(void *)=%d\n",len);
%dでgccに警告されたら、
%ldではなくて、
%zu(10進),%zx(16進)を検討してください。
■(int)sizeof(x)とキャストしてはダメ
printf("sizeof(big_data)=%d\n",(int)sizeof(big_data));
64bit情報が
(int)sizeofキャストによって
32bitに欠損してしまいます。
キャストするのではなく
書式%dを見直して下さい。
■ILP32で許容されていたキャスト
int i = 0x11223344 ;
//ILP32時代は許容されたがLP64ではダメ
long_set((long *)&i);
printf("%x\n",i);
ILP32時代はintとlongが
共に32bit-4byteだったので、
適当にキャストしても許容されました。
LP64時代は
intは32bit-4byte
longは64bit-8byteなのでこの例のように
適当にキャストすると
メモリ破壊が発生します。
根本対策としては
キャストしてコンパイラを黙らせるのではなく、
キャストしなくてもコンパイラに怒られないように
型を見直す必要があります。
■プロトタイプ宣言必須
printf("%s\n",strerror(0));
LP64時代はポインタは64bit-8byteです。
標準関数 strerror()のように
アドレスを返す関数をプロトタイプ宣言無しで使用するとint型32bitを返却する関数とコンパイラに解釈されます。
このためアドレスの上位32bitが欠損し不正なアドレスを参照するので、プログラムが異常終了する可能性があります。
■まとめ(コンパイル出来ても動かない例)
#include <stdio.h>
#include <limits.h>
//■1.long 32bit=4byteではなくなった
typedef unsigned short U2;
typedef unsigned long U4;
//■2.構造体の中の long
typedef struct {
U2 d1; //2byte
U2 d2; //2byte
//ここに穴が開く
long data ; //4byteではなくなった
} tag_t ;
typedef struct {
int id;
char buf[INT_MAX];
} big_data ;
static void long_set(long *ptr){
*ptr = 0x7766554433 ;
}
int main(void){
//■3.%dではなくて%zu,%zx
printf("sizeof(int)=%d\n", sizeof(int));
printf("sizeof(tag_t)=%d\n",sizeof(tag_t));
printf("sizeof(U4)=%d\n", sizeof(U4));
size_t len = sizeof(void *);
printf("sizeof(void *)=%d\n",len);
//■4.キャストしてはダメだ(その1)
//64bit情報が(int)キャストによって欠損してしまう
printf("sizeof(big_data)=%d\n",(int)sizeof(big_data));
//■5.キャストしてはダメだ(その2)
int i = 0x11223344 ;
//ILP32時代は許容されたがLP64ではダメ
long_set((long *)&i);
printf("%x\n",i);
//■6.プロトタイプ宣言なしで使ってはダメだ
printf("%s\n",strerror(0));
}