【汎整数拡張】第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進数はあくまで表記の違いなだけで、その表現される数学的値とは無関係です。当たり前のことですが、ここを区別しておきたいものです。