【汎整数拡張】第1回 内部表現と数学的値
「C言語はポインタが難しい」というのが定説ですが、ポインタは四天王の中でも最弱だと思っています。ポインタ以上に難解なのが、この「汎整数拡張」。インテグラル・プロモーション(integral promotion)とも言います。
汎整数拡張の挙動を確認する上で、ビットレベルで値を確認するのは必須ですが、そういう標準ライブラリは存在しないので、これは自作します。引数numに二進数表示した整数値を指定し、lenには、その整数型のビット幅をsizeof演算子で求めた値を指定すると、指定されたビット幅での二進数表現が表示されます。
4 void bit_dump(unsigned int num, size_t len){ 5 6 size_t bitlen = len*8; 7 unsigned int mask; 8 9 for(bitlen=len*8; bitlen>0; bitlen--){ 10 mask = (1<<(bitlen-1)); 11 printf("%d", !!(num&mask)); 12 printf("%c", (bitlen%4==1 && bitlen>0 ?' ':'\0')); 13 } 14 printf("\n"); 15 16 return; 17 }
今回はchar型にしぼって、char型/unsigned char型の-1/255、異種型の値を互いに代入したもの、異種型にキャストしたもの、のそれぞれのビットパターンを確認してみます。
19 int main(int argc, char **argv){ 20 21 char c = -1; 22 unsigned char uc = 255; 23 24 /* 25 値の表示 26 */ 27 printf("> -1 : char\n"); 28 bit_dump(c, sizeof(c)); 29 printf("\n"); 30 31 printf("> 255 : unsigned char\n"); 32 bit_dump(uc, sizeof(uc)); 33 printf("\n"); 34 35 /* 36 代入 37 */ 38 uc = c; 39 40 printf("> unsigned char = char\n"); 41 bit_dump(uc, sizeof(uc)); 42 printf("\n"); 43 44 c = uc; 45 46 printf("> char = unsigned char\n"); 47 bit_dump(c, sizeof(c)); 48 printf("\n"); 49 50 /* 51 明示的キャスト 52 */ 53 printf("> (unsigned char)char\n"); 54 bit_dump((unsigned char)c, sizeof((unsigned char)c)); 55 printf("\n"); 56 57 printf("> (char)unsigned char\n"); 58 bit_dump((char)uc, sizeof((char)uc)); 59 printf("\n"); 60 61 return 0; 62 }
実行結果
> -1 : char 1111 1111 > 255 : unsigned char 1111 1111 > unsigned char = char 1111 1111 > char = unsigned char 1111 1111 > (unsigned char)char 1111 1111 > (char)unsigned char 1111 1111
charの-1、unsigned charの255、それぞれの値を入れ替えて代入した変数の値、それぞれの型を入れ替えてキャストした値の内部表現は、いずれも「1111 1111」となります。以前も書きましたが、(char)-1〜-128と(unsigned char)255〜128は、同じビットパターンを共有しています。
-128〜127、0〜255のビットの動き
このビットパターン「1111 1111」をcharとして解釈すれば、数学的値は「-1」、unsigned char型としては「255」となります。「1111 1111」というビットパターンのことを「内部表現」と呼称します。この内部表現を特定の型で解釈した-1や255の値のことを「数学的値」(便宜的に10進数で表現)と呼称します。
同じビットパターンでも、「型」という解釈によって、その数学的値が異なるということは、内部表現(ビットパターン)と型の組み合わせで、その数学的値は決定されることになります。このビットパターンを16進数で表現すれば「FF」と表記されるわけですが、2進数/16進数はあくまで表記の違いなだけで、その表現される数学的値とは無関係です。当たり前のことですが、ここを区別しておきたいものです。