【汎整数拡張】第2回 単項変換
前回は、以下のことを確認しました。
- 基本形が同じならば、signed/unsigendでキャストしようが、その内部表現(ビット列)は変化しない。
- 内部表現が同じであっても、その数学的値が同じとは限らない。
今回は、char/unsigned charをint/unsigned intにそれぞれ型変換した場合、その内部表現はどうなるかという実験です。
19 int main(int argc, char **argv){ 20 21 char c = -1; 22 unsigned char uc = 255; 23 24 /* 25 変換なし char/uchar 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 単項変換 char/uchar -> int 37 */ 38 printf("> -1 : +(char)\n"); 39 bit_dump(+c, sizeof(+c)); 40 printf("\n"); 41 42 printf("> 255 : +(unsigned char)\n"); 43 bit_dump(+uc, sizeof(+uc)); 44 printf("\n"); 45 46 /* 47 明示的キャスト 48 */ 49 printf("> (int)char\n"); 50 bit_dump((int)c, sizeof((int)c)); 51 printf("\n"); 52 53 printf("> (unsigned int)char\n"); 54 bit_dump((unsigned int)c, sizeof((unsigned int)c)); 55 printf("\n"); 56 57 printf("> (int)unsigned char\n"); 58 bit_dump((int)uc, sizeof((int)uc)); 59 printf("\n"); 60 61 printf("> (unsigned int)unsigned char\n"); 62 bit_dump((unsigned int)uc, sizeof((unsigned int)uc)); 63 printf("\n"); 64 65 return 0; 66 }
実行結果
> -1 : char 1111 1111 > 255 : unsigned char 1111 1111 > -1 : +(char) 1111 1111 1111 1111 1111 1111 1111 1111 > 255 : +(unsigned char) 0000 0000 0000 0000 0000 0000 1111 1111 > (int)char 1111 1111 1111 1111 1111 1111 1111 1111 > (unsigned int)char 1111 1111 1111 1111 1111 1111 1111 1111 > (int)unsigned char 0000 0000 0000 0000 0000 0000 1111 1111 > (unsigned int)unsigned char 0000 0000 0000 0000 0000 0000 1111 1111
int型より精度の低い型(char型など)が式中で評価された場合、その型はint型に型昇格します。int型で表現できない場合、unsigned int型に型昇格します。これを単項変換と呼称します。
例えば、char型オブジェクトの先頭に単項+演算子をつけると、char型オブジェクトは式の一部として評価されるため、int型に昇格します。その結果、内部表現のビット長は、int型のバイトサイズ(以下の場合は4バイト=32ビット)に伸長されます。
> -1 : +(char) 1111 1111 1111 1111 1111 1111 1111 1111 > 255 : +(unsigned char) 0000 0000 0000 0000 0000 0000 1111 1111
結果型のビット長が大きい場合、上位ビットに空きができるわけですが、その空きビットを0/1のいずれで埋めるか、という問題があります。
- char→intの型昇格においては、符号ビットの値と同じビットで埋める。
- unsigned char→intの型昇格においては、0で埋める。
これは、「型昇格前と型昇格後では、その数学的値は変化しない」と考えれば道理ですが…
そうすると、負数のchar→unsigned intへの型昇格ではどうなるか。そもそもunsigned intには符号の概念が無いので、負数をどうやって表現するのか、という問題が出てきます。これについては、以下の通りとなります。
> (int)char 1111 1111 1111 1111 1111 1111 1111 1111 > (unsigned int)char 1111 1111 1111 1111 1111 1111 1111 1111
char→intへの型昇格は先ほどの単項変換の通りだとして、char→unsigned intへの型昇格においては、
char → int → unsigned int
という二段階の型昇格が行われます。int→unsigned intへの型昇格においては、同じビット長なので内部表現は変化しない。結果として、char→unsigned intへの型変換は、char→intへの変換と同様となります。
unsigned char→int/unsigned intへの型変換は、以下の通り、空きビットをただ0で埋めるだけです。
> (int)unsigned char 0000 0000 0000 0000 0000 0000 1111 1111 > (unsigned int)unsigned char 0000 0000 0000 0000 0000 0000 1111 1111
空きビットに何がセットされるかをまとめたのが、以下の表です。
(行見出しは昇格前の型。列見出しは昇格後の型。-は空きビットが発生しないことを示す)
char型 | uchar型 | int型 | uint型 | |
---|---|---|---|---|
char型 | - | - | 元の符号ビット | 元の符号ビット |
uchar型 | - | - | 0 | 0 |
こうして見ると、型昇格後の空きビットに何がセットされるかは「昇格前の型(の符号修飾子)」に依存することが分かります。
char型変数に-1を代入すると、内部表現としては「1111 1111」となりますが、それをunsigned intに型昇格する際、その数学的値は「255」とは解釈されないことに注意したいです。charはいったんintに型昇格しますが、int→unsigned intの型昇格においては内部表現は変化しないため、その数学的値は
(char)-1 → (int)-1 → (unsigned int)2^64-1
と変化します。