【C言語】
0xffffffffとは-1か?4294967295か?

warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]

警告:符号の異なる整数式の比較


この記事の概要

この記事では、C言語の0xFFFFFFFFが
符号付き整数では-1に、
符号なし整数では4294967295として扱われる理由を解説しています。
暗黙の型変換や符号拡張の挙動を詳述し、
バグ防止のために型変換を理解する重要性を
具体例を用いて解説しています。


この記事の環境

ここでの解説はLP64-gcc/clangの環境を前提とします
(いわゆるlinux系64bitコンパイラ環境)。
8,16,32,128bitやLLP64
(いわゆるWindows系 64bitコンパイラ環境)は
考慮していません。

■奇妙なif文の実行結果を予想して下さい

●Q1:0xFFFFffffはマイナス壱と等しいのに正の巨大な数とも等しい?

#include <stdio.h>
//0xFFFFffffはマイナス壱と等しいのに
//正の巨大な数とも等しい?
int main(void){
    if((0xFFFFffff==-1)&&(0xFFFFffff==4294967295))
        puts("YES");
    else
        puts("NO");
}

実行結果


●Q2:0xFFFFffffはマイナス壱と等しいのに零以上か?

#include <stdio.h>
//0xFFFFffffはマイナス壱と等しいのに零以上か?
int main(void){
    if((0xFFFFffff==-1)&&(0xFFFFffff>=0))
        puts("YES");
    else
        puts("NO");
}

実行結果


●Q3:16進数と10進数の足し算の結果は同じか?

#include <stdio.h>
//16進数と10進数の足し算の結果は同じか?
int main(void){
    if((0xFFFFffff+1)==(4294967295+1))
        puts("YES");
    else
        puts("NO");
}

実行結果


●Q4:if((x==y)&&(y==z)&&(x!=z))は成り立つか?

#include <stdio.h>
//if((x==y)&&(y==z)&&(x!=z))は成り立つか?
int main(void){
    int             x = -1;
    unsigned int    y = 0xFFFFffff ;
    long            z = 4294967295 ;
    if((x == y) && (y == z) && (x != z))
        puts("YES");
    else
        puts("NO");
}

実行結果


■定数の型を文字列で表示する方法とは?

#include <stdio.h>
#define typename(x) _Generic((x),\
    char:       "char",\
    short:      "short",\
    int:        "int",\
    unsigned int:"unsigned int",\
    long:       "long",\
    default:    "other"\
)
int main(void){
    printf("%s %zu\n",typename(-1),sizeof(-1));
    printf("%s %zu\n",typename(0),sizeof(0));
    printf("%s %zu\n",typename(1),sizeof(1));
    printf("%s %zu\n",typename(0xFFFFffff),sizeof(0xFFFFffff));
    printf("%s %zu\n",typename(4294967295),sizeof(4294967295));
}

注意:
_GenericはC11で採用された新しい機能なので古いコンパイラでは動きません。

●実行結果

./a.out 
int 4
int 4
int 4
unsigned int 4
long 8

●-1とは?

・int型4byteの負の定数です

●0xffffffffとは?

・unsigned int型4byteの正の定数です

●4294967295とは?

・long型8byteの正の定数です


■0xffffffffと-1のビットパターンは同じ

#include <stdio.h>
#include <string.h>
#include <stdint.h>
int     main(void){
    int32_t     s = -1 ;
    uint32_t    u = 0xffffffff ;
    if(memcmp(&s,&u,sizeof(s)) == 0){
        printf("%d と %x のビットパターンは同じ\n",s,u);
    }else{
        puts("ここには来ない");
    }
}

➡LP64での実行結果

./a.out
-1 と ffffffff のビットパターンは同じ

■-1と0xffffffffと4294967295と暗黙の型変換

・if(0xffffffff == -1)のような
符号無し(0xffffffff)と符号付き(-1)の比較は
符号付き-1のほうが符号無しに拡張され
双方同じ符号無しになってから比較されます。

簡単に言うと演算子の両辺の型が違う場合
狭い型は
広い型に格上げされてから演算されます。

このため0xffffffffと -1は等しくなります。

先のQ4で発生している暗黙の型変換を
明示すると次の型変換が発生しています。

#include <stdio.h>
//if((x==y)&&(y==z)&&(x!=z))は成り立つか?
int main(void){
    int             x = -1;
    unsigned int    y = 0xFFFFffff ;
    long            z = 4294967295 ;
    if(((unsigned int)x == y) && ((long)y == z) && ((long)x != z))
        puts("YES");
    else
        puts("NO");
}

参考:

INT02-C. 整数変換のルールを理解する

CWE-194: Unexpected Sign Extension