【C言語】
32bit➡64bit移行時の注意点
(gcc版)

gcc版 32➡64bit移行

■longは64bit=8byteになった

typedef unsigned short  U2; 
typedef unsigned long   U4;//要変更

gcc LP64モードで long は64bit=8byteです。
U4の様な型定義はサイズが変わってしまいます。


intは32bit=4byteのまま

#include <stdio.h>
int main(void){
    printf("sizeof(int)  = %zu\n",sizeof(int));
    printf("sizeof(long) = %zu\n",sizeof(long));
}

➡実行結果

./a.out
sizeof(int)  = 4
sizeof(long) = 8

gcc -m64等の
LP64モードコンパイラの場合
int型は32bitの4byteのままです。


LLP64モードコンパイラの場合
long型は
32bitの4byteです。
64bitの8byteではありません。


■構造体の中の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が欠損し不正なアドレスを参照するので、プログラムが異常終了する可能性があります。


32bitで動いても64bitで動かなくなる例

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