【C言語】64bitへ移行(gcc版)


■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));
}