シンボル数とシンボル数のない強制タイプ変換の問題

3318 ワード

C言語で符号数が符号なしに変換されると、いくつかの問題が発生します.まず、以下のプログラムの例を見てください.
int main()
{
	char ch[12] = {0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00};
	unsigned int result = (ch[7] << 8) + ch[6];     // ch[7] 0x0F, ch[6] 0xFF
	printf("result(0x%x), ch[7](0x%x), ch[6](0x%x)
", result, ch[7], ch[6]); return 0; }

計算結果はresult=0 xFFFと思っていたのですが、実際の計算結果はresult=0 xEFFでした.プログラムの出力は次のとおりです.
従来ch[6]はcharがunsigned intに移行すると0 xffから0 xffffffに移行する.その符号ビット(最上位)は1であり、符号なし数に変換すると他のビット(9位から32位)をすべて1にする.したがって,最後の計算resultの結果は0 xFFFではなく0 xEFFである.逆アセンブリを見てみると、もっとはっきりしています.
	unsigned int result = (ch[7] << 8) + ch[6];
00411C68  movsx       eax,byte ptr [ebp-0Dh] 
00411C6C  shl         eax,8 
00411C6F  movsx       ecx,byte ptr [ebp-0Eh] 
00411C73  add         eax,ecx 
00411C75  mov         dword ptr [ebp-20h],eax 

movsx命令を用いてデータの伝送が行われていることが上から分かる.movsxはシンボル拡張伝送命令であり、シンボル拡張とは拡張されたビットをシンボルビットの値で補完することを意味する.8ビットのデータ0 xffのように、32ビットに変換されたデータは0 xffffffである(シンボルビットが1であるため).8ビットのデータ0 x 3 aのように、変換された値は0 x 3 aである(符号が0であるため).
逆送金の編集後、変換の過程を見ました.1.8ビットの符号数を32ビットの符号数2に拡張する.拡張32ビットの符号数をシフト加算する.シンボル数をシンボル数なしで表示
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
異なるバイト数の符号なし変換例を見てみましょう.
int main()
{
	unsigned char ch[12] = {0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00};
	unsigned int result = (ch[7] << 8) + ch[6];
	printf("result(0x%x), ch[7](0x%x), ch[6](0x%x)
", result, ch[7], ch[6]); return 0; }

上記のプログラムは正しい答えresult=0 xfffを出力することができ、以下はプログラムの実行結果である.
では、なぜここから出力された結果が正しいのでしょうか.逆アセンブリの結果を見てみましょう.
	unsigned int result = (ch[7] << 8) + ch[6];
00411C68  movzx       eax,byte ptr [ebp-0Dh] 
00411C6C  shl         eax,8 
00411C6F  movzx       ecx,byte ptr [ebp-0Eh] 
00411C73  add         eax,ecx 
00411C75  mov         dword ptr [ebp-20h],eax 

8ビットの符号なし数から32ビットの符号なし数に変換するにはmovzx(0拡張転送命令付き)を用いた.変換中にeaxの9〜32番目の位置が0でないため、最終的に正しい結果が得られる.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最初の例を変更して、result値を計算するときにcharタイプのch[7]とch[6]をunsigned charに強制的に変換して、最後に結果が得られるかどうかを見てみましょう.次に、修正されたコードを示します.
int main()
{
	char ch[12] = {0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00};
	unsigned int result = (((unsigned char) ch[7]) << 8) + ((unsigned char)ch[6]);
	printf("result(0x%x), ch[7](0x%x), ch[6](0x%x)
", result, (unsigned char)ch[7], (unsigned char)ch[6]); return 0; }

最後の結果は2番目の例の結果と一致し,我々が望んでいる結果である.逆アセンブリ後のコードを見てみましょう.
unsigned int result = (((unsigned char) ch[7]) << 8) + ((unsigned char)ch[6]);
00411C68  movzx       eax,byte ptr [ebp-0Dh] 
00411C6C  shl         eax,8 
00411C6F  movzx       ecx,byte ptr [ebp-0Eh] 
00411C73  add         eax,ecx 
00411C75  mov         dword ptr [ebp-20h],eax 

この逆アセンブリコードは、第2の例の逆アセンブリ符号化と一致する.ch[7]をunsigned charに強制的に移行するため、転送命令はmovzxを使用する.