さんじゅつろんりビットえんざん

16245 ワード

アルゴリズム結果オーバーフロー:
シンボルレス数がオーバーフローすると最大数から最小数に変わります
シンボル数オーバーフローによりシンボルビットが変更されます
 
#include 

using namespace std;

int main()
{
	for(int i = 1; i > 0 ; i += 1)
	{
		printf("%d \r
",i); } return 0; }

上のコードはデッドサイクルのように見えますが、実はそうではありません.主な原因はiが符号付き整数で、iの最大値は0 x 7 FFFFFFFで実行され、1を加えると最終的に0 x 800000になります.これは最高位が1で、負数なので、飛び出します.
 
オーバーフローは、データがキャリーされた後にデータの保存範囲を超えたためです.
 
キャリー:シンボル数が記憶範囲を超えていないことをキャリーと呼びます.符号ビットがないため、データを破壊することはなく、より多く出た1ビットデータはキャリーフラグビットCFに保存され、データはキャリーを生成し、キャリー後CFに保存されるだけである.CFで確認できますし、キャリーの有無も判断できます
 
オーバーフロー:シンボル数が記憶範囲を超えていることをオーバーフローといい、データがキャリーされるため、シンボル数のある最上位-シンボルビットが破壊される.オーバーフローはシンボル数のみです.OFを確認し、オーバーフローしていないか確認できます.OF判定は,計算に関与する数値記号が一致し,計算結果記号が異なる場合にはOFが成立すると判定する.
 
他の操作命令によりオーバーフローやキャリーが発生する場合もあります
 
 
自己増加と自己減少:
 
みんなはすべて自増と自減演算を熟知して、同じく法則を知っています:前置と後置、つまり先に自増後演算と先演算後自増
コードで見てみましょう
 
ソース:
#include 

using namespace std;

int main(int argc,char *argv[])
{
	int nVarOne = argc;
	int nVarTwo = argc;

	nVarTwo = 5 + (nVarOne ++);
	nVarTwo = 5 + (++ nVarOne);

	nVarOne = 5 + (nVarTwo --);
	nVarOne = 5 + (-- nVarTwo);

	return 0;
}

 
アセンブリコード:
 
1:    #include 
2:
3:    using namespace std;
4:
5:    int main(int argc,char *argv[])
6:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,48h
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-48h]
0040125C   mov         ecx,12h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
7:        int nVarOne = argc;
00401268   mov         eax,dword ptr [ebp+8]
0040126B   mov         dword ptr [ebp-4],eax
8:        int nVarTwo = argc;
0040126E   mov         ecx,dword ptr [ebp+8]
00401271   mov         dword ptr [ebp-8],ecx
9:
10:       nVarTwo = 5 + (nVarOne ++);
00401274   mov         edx,dword ptr [ebp-4]
00401277   add         edx,5
0040127A   mov         dword ptr [ebp-8],edx
0040127D   mov         eax,dword ptr [ebp-4]
00401280   add         eax,1
00401283   mov         dword ptr [ebp-4],eax
11:       nVarTwo = 5 + (++ nVarOne);
00401286   mov         ecx,dword ptr [ebp-4]
00401289   add         ecx,1
0040128C   mov         dword ptr [ebp-4],ecx
0040128F   mov         edx,dword ptr [ebp-4]
00401292   add         edx,5
00401295   mov         dword ptr [ebp-8],edx
12:
13:       nVarOne = 5 + (nVarTwo --);
00401298   mov         eax,dword ptr [ebp-8]
0040129B   add         eax,5
0040129E   mov         dword ptr [ebp-4],eax
004012A1   mov         ecx,dword ptr [ebp-8]
004012A4   sub         ecx,1
004012A7   mov         dword ptr [ebp-8],ecx
14:       nVarOne = 5 + (-- nVarTwo);
004012AA   mov         edx,dword ptr [ebp-8]
004012AD   sub         edx,1
004012B0   mov         dword ptr [ebp-8],edx
004012B3   mov         eax,dword ptr [ebp-8]
004012B6   add         eax,5
004012B9   mov         dword ptr [ebp-4],eax
15:
16:       return 0;
004012BC   xor         eax,eax
17:   }
004012BE   pop         edi
004012BF   pop         esi
004012C0   pop         ebx
004012C1   mov         esp,ebp
004012C3   pop         ebp
004012C4   ret

アセンブリレベルのコード実装は、自己増加と自己減少の実装を明確に示しています.
 
リレーショナル演算と論理演算:
 
両者の関係比較は種々のタイプのジャンプにより実現でき,比較結果に影響されるフラグビットに基づいて対応する条件ジャンプ命令を選択できる.
ジャンプ命令はみんなよく知っているから、くどくどしないでください.
 
エクスプレッションショート&:
 
ソース:
#include 

using namespace std;

int Accumulation(int nNumber)
{
	nNumber && (nNumber += Accumulation(nNumber - 1));
	return nNumber;
}

int main(int argc,char *argv[])
{
	Accumulation(12);

	return 0;
}

アセンブリコード分析:
 
1:    #include 
2:
3:    using namespace std;
4:
5:    int Accumulation(int nNumber)
6:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,40h
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-40h]
0040125C   mov         ecx,10h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
7:        nNumber && (nNumber += Accumulation(nNumber - 1));
00401268   cmp         dword ptr [ebp+8],0
0040126C   je          Accumulation+35h (00401285)
0040126E   mov         eax,dword ptr [ebp+8]
00401271   sub         eax,1
00401274   push        eax
00401275   call        @ILT+10(Accumulation) (0040100f)
0040127A   add         esp,4
0040127D   mov         ecx,dword ptr [ebp+8]
00401280   add         ecx,eax
00401282   mov         dword ptr [ebp+8],ecx
8:        return nNumber;
00401285   mov         eax,dword ptr [ebp+8]
9:    }
00401288   pop         edi
00401289   pop         esi
0040128A   pop         ebx
0040128B   add         esp,40h
0040128E   cmp         ebp,esp
00401290   call        __chkesp (004081b0)
00401295   mov         esp,ebp
00401297   pop         ebp
00401298   ret

 
10:
11:   int main(int argc,char *argv[])
12:   {
004012B0   push        ebp
004012B1   mov         ebp,esp
004012B3   sub         esp,40h
004012B6   push        ebx
004012B7   push        esi
004012B8   push        edi
004012B9   lea         edi,[ebp-40h]
004012BC   mov         ecx,10h
004012C1   mov         eax,0CCCCCCCCh
004012C6   rep stos    dword ptr [edi]
13:       Accumulation(12);
004012C8   push        0Ch
004012CA   call        @ILT+10(Accumulation) (0040100f)
004012CF   add         esp,4
14:
15:       return 0;
004012D2   xor         eax,eax
16:   }
004012D4   pop         edi
004012D5   pop         esi
004012D6   pop         ebx
004012D7   add         esp,40h
004012DA   cmp         ebp,esp
004012DC   call        __chkesp (004081b0)
004012E1   mov         esp,ebp
004012E3   pop         ebp
004012E4   ret

主に短絡状況を見る:第1段のコード分析から&&前の式が再帰的に飛び出す条件であることがわかる
これはまさに短絡現象を利用してコードを精錬させたものである.
 
演算子のショートカットを見てみましょう.
 
ソースコードは基本的に同じで、アセンブリ対照コードを直接見ます.
 
1:    #include 
2:
3:    using namespace std;
4:
5:    int Accumulation(int nNumber)
6:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,40h
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-40h]
0040125C   mov         ecx,10h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
7:        (nNumber==0) || (nNumber += Accumulation(nNumber - 1));
00401268   cmp         dword ptr [ebp+8],0
0040126C   je          Accumulation+35h (00401285)
0040126E   mov         eax,dword ptr [ebp+8]
00401271   sub         eax,1
00401274   push        eax
00401275   call        @ILT+10(Accumulation) (0040100f)
0040127A   add         esp,4
0040127D   mov         ecx,dword ptr [ebp+8]
00401280   add         ecx,eax
00401282   mov         dword ptr [ebp+8],ecx
8:        return nNumber;
00401285   mov         eax,dword ptr [ebp+8]
9:    }
00401288   pop         edi
00401289   pop         esi
0040128A   pop         ebx
0040128B   add         esp,40h
0040128E   cmp         ebp,esp
00401290   call        __chkesp (004081b0)
00401295   mov         esp,ebp
00401297   pop         ebp
00401298   ret

 
10:
11:   int main(int argc,char *argv[])
12:   {
004012B0   push        ebp
004012B1   mov         ebp,esp
004012B3   sub         esp,40h
004012B6   push        ebx
004012B7   push        esi
004012B8   push        edi
004012B9   lea         edi,[ebp-40h]
004012BC   mov         ecx,10h
004012C1   mov         eax,0CCCCCCCCh
004012C6   rep stos    dword ptr [edi]
13:       Accumulation(12);
004012C8   push        0Ch
004012CA   call        @ILT+10(Accumulation) (0040100f)
004012CF   add         esp,4
14:
15:       return 0;
004012D2   xor         eax,eax
16:   }
004012D4   pop         edi
004012D5   pop         esi
004012D6   pop         ebx
004012D7   add         esp,40h
004012DA   cmp         ebp,esp
004012DC   call        __chkesp (004081b0)
004012E1   mov         esp,ebp
004012E3   pop         ebp
004012E4   ret

比較すると,異なる論理演算子を用いたが,同じアセンブリコードを生成し,それらの機能が完全に同じであることを示した.
 
条件式:
 
三目表現はよく知られていませんが、Cの中には一つしかありません.
式1?式2:式3
 
条件式には4つの変換スキームがあります.
>シナリオ1:式1は単純な比較であり、式2と式3の差は1に等しい
>シナリオ2:式1は単純な比較であり、式2と式3の差は1より大きい
>シナリオ3:式1は複雑な比較であり、式2と式3の差は1より大きい
>シナリオ4:式2と式3は変数であり、最適化されていない
 
例:
シナリオ1:
ソース:
int Condition(int argc,int n)
{
	return argc == 5 ? 5 : 6;
}

アセンブリコード:
00401678	xor eax,eax
0040167A	cmp dword ptr [ebp+8],5
0040167E	setne  al        //           0,  al,   0
00401681	add eax,5

 
しかしsetneも1の差のバランスにしか使えず、1より大きいと無力になります
シナリオ2:
ソース:
int Condition(int argc,int n)
{
	return argc == 5 ? 4 : 10;
}

アセンブリコード:
00401678	mov	eax,dword ptr [ebp+8]
0040167B	sub eax,5
0040167E	neg	eax
00401680	sbb	eax,eax
00401682	and eax,6
00401685	add	eax,4

arg=5という等値比較では,VC++は減算と補正演算を用いて真値か否かを判断する.
argcが5に等しくない限り、sub命令を実行した後、eaxの値は0ではない.
そしてneg命令によりeaxの符号ビットが変化し,CFが1となる.
次に、ビット減算eax=eax−eax−CFを実行する.CFが1の場合、eaxの値は0 xFFFFFFFFFF、そうでない場合は0
eaxと6を使用してビットと演算を行った後、eaxの数値が元の値-1であれば、解果は6、プラス4は数値10に等しい.
そうでない場合argc==5、eaxは常に0で、4を加えて数値4になります.
ただし、式1が1区間である場合は、これではだめです
 
シナリオ3:
ソース:
int Condition(int argc,int n)
{
	return argc <= 8 ? 4 : 10;
}

アセンブリコード分析:
00401678	xor  eax,eax            //  eax
0040167A	cmp	 dword ptr [ebp+8],8      //  argc 8
0040167E	setg al              //  argc>8  al=1,   0
00401681	dec	 eax             //eax  1,eax  =0xFFFFFFFF 0
00401682	and  al,0FAh         //eax   0xFFFFFFFA(-6  ) 0
00401685	add	eax,0Ah          //eax   4 10

上はもうはっきり説明した.
ただし、式2または式3の値が未知数の場合、従来の方法では最適化できません.
コンパイラは通常の文の流れに基づいて比較し、判断します.
 
シナリオ4:
ソース:
int Condition(int argc,int n)
{
	return argc ? 4 : n;
}

アセンブリ言語解析:
00401448	cmp	dword ptr [ebp+8],0
0040144C	je	Condition+27h (00401457)

0040144E	mov dword ptr [ebp-4],8
00401455	jmp	Condition+2Dh (0040145d)

00401457	mov eax,dword ptr [ebp+0Ch]
0040145A	mov	dword ptr [ebp-4],eax

0040145D	mov eax,dword ptr [ebp-4]

00401466    ret

ビット演算
 
<<左シフト演算,最高位左シフトCF,最低位補零
>>右シフト演算、最高位不変、最低位右シフトCF中
 
コードがどのように実装されているかを見てみましょう.
 
ソース:
#include 

using namespace std;

int BitOperation(int argc) 
{
	argc = argc << 3;
	argc = argc >> 5;
	argc = argc | 0xFFFF0000;
	argc = argc & 0x0000FFFF;
	argc = argc ^ 0xFFFF0000;
	argc = ~argc;

	return argc;
}

int main()
{
	int a = 5;
	BitOperation(a);

	return 0;
}

アセンブリコード分析:
 
1:    #include 
2:
3:    using namespace std;
4:
5:    int BitOperation(int argc)
6:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,40h
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-40h]
0040125C   mov         ecx,10h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
7:        argc = argc << 3;
00401268   mov         eax,dword ptr [ebp+8]
0040126B   shl         eax,3
0040126E   mov         dword ptr [ebp+8],eax
8:        argc = argc >> 5;
00401271   mov         ecx,dword ptr [ebp+8]
00401274   sar         ecx,5
00401277   mov         dword ptr [ebp+8],ecx
9:        argc = argc | 0xFFFF0000;
0040127A   mov         edx,dword ptr [ebp+8]
0040127D   or          edx,0FFFF0000h
00401283   mov         dword ptr [ebp+8],edx
10:       argc = argc & 0x0000FFFF;
00401286   mov         eax,dword ptr [ebp+8]
00401289   and         eax,0FFFFh
0040128E   mov         dword ptr [ebp+8],eax
11:       argc = argc ^ 0xFFFF0000;
00401291   mov         ecx,dword ptr [ebp+8]
00401294   xor         ecx,0FFFF0000h
0040129A   mov         dword ptr [ebp+8],ecx
12:       argc = ~argc;
0040129D   mov         edx,dword ptr [ebp+8]
004012A0   not         edx
004012A2   mov         dword ptr [ebp+8],edx
13:
14:       return argc;
004012A5   mov         eax,dword ptr [ebp+8]
15:   }
004012A8   pop         edi
004012A9   pop         esi
004012AA   pop         ebx
004012AB   mov         esp,ebp
004012AD   pop         ebp
004012AE   ret

 
16:
17:   int main()
18:   {
004012D0   push        ebp
004012D1   mov         ebp,esp
004012D3   sub         esp,44h
004012D6   push        ebx
004012D7   push        esi
004012D8   push        edi
004012D9   lea         edi,[ebp-44h]
004012DC   mov         ecx,11h
004012E1   mov         eax,0CCCCCCCCh
004012E6   rep stos    dword ptr [edi]
19:       int a = 5;
004012E8   mov         dword ptr [ebp-4],5
20:       BitOperation(a);
004012EF   mov         eax,dword ptr [ebp-4]
004012F2   push        eax
004012F3   call        @ILT+10(BitOperation) (0040100f)
004012F8   add         esp,4
21:
22:       return 0;
004012FB   xor         eax,eax
23:   }
004012FD   pop         edi
004012FE   pop         esi
004012FF   pop         ebx
00401300   add         esp,44h
00401303   cmp         ebp,esp
00401305   call        __chkesp (004081d0)
0040130A   mov         esp,ebp
0040130C   pop         ebp
0040130D   ret

上の逆アセンブリコードは比較的簡単で、記号のあるビット演算です.
次の記号のないものは少し違います.
 
ソース:
#include 

using namespace std;

void BitOperation(int argc) 
{
	unsigned int nVar = argc;
	nVar <<= 3;
	nVar >>= 5;

}

int main()
{
	int a = 5;
	BitOperation(a);

	return 0;
}

アセンブリコード分析:
 
1:    #include 
2:
3:    using namespace std;
4:
5:    void BitOperation(int argc)
6:    {
00401250   push        ebp
00401251   mov         ebp,esp
00401253   sub         esp,44h
00401256   push        ebx
00401257   push        esi
00401258   push        edi
00401259   lea         edi,[ebp-44h]
0040125C   mov         ecx,11h
00401261   mov         eax,0CCCCCCCCh
00401266   rep stos    dword ptr [edi]
7:        unsigned int nVar = argc;
00401268   mov         eax,dword ptr [ebp+8]
0040126B   mov         dword ptr [ebp-4],eax
8:        nVar <<= 3;
0040126E   mov         ecx,dword ptr [ebp-4]
00401271   shl         ecx,3
00401274   mov         dword ptr [ebp-4],ecx
9:        nVar >>= 5;
00401277   mov         edx,dword ptr [ebp-4]
0040127A   shr         edx,5
0040127D   mov         dword ptr [ebp-4],edx
10:
11:   }
00401280   pop         edi
00401281   pop         esi
00401282   pop         ebx
00401283   mov         esp,ebp
00401285   pop         ebp
00401286   ret

 
12:
13:   int main()
14:   {
004012A0   push        ebp
004012A1   mov         ebp,esp
004012A3   sub         esp,44h
004012A6   push        ebx
004012A7   push        esi
004012A8   push        edi
004012A9   lea         edi,[ebp-44h]
004012AC   mov         ecx,11h
004012B1   mov         eax,0CCCCCCCCh
004012B6   rep stos    dword ptr [edi]
15:       int a = 5;
004012B8   mov         dword ptr [ebp-4],5
16:       BitOperation(a);
004012BF   mov         eax,dword ptr [ebp-4]
004012C2   push        eax
004012C3   call        @ILT+10(BitOperation) (0040100f)
004012C8   add         esp,4
17:
18:       return 0;
004012CB   xor         eax,eax
19:   }
004012CD   pop         edi
004012CE   pop         esi
004012CF   pop         ebx
004012D0   add         esp,44h
004012D3   cmp         ebp,esp
004012D5   call        __chkesp (004081a0)
004012DA   mov         esp,ebp
004012DC   pop         ebp
004012DD   ret