算术逻辑位运算

算法结果溢出:

一个无符号数产生溢出后会从一个最大数变为最小数

有符号数溢出会修改符号位

 

#include 

using namespace std;

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

	return 0;
}


上面代码看上去像一个死循环,其实不然,主要原因是i为有符号整数,i的最大值运行为0x7FFFFFFF,在加1时最终会变成0x8000000,这是最高位为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:    #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中的值将为0xFFFFFFFF,否则为0

使用eax与6做位与运算后,如果eax中数值原来为-1,则解果为6,加4等于数值10

否则argc == 5 , eax始终为0,加4为数值4

但是,当表达式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


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(C++反汇编)