算术逻辑位运算

来源:互联网 发布:e8票据打印软件 编辑:程序博客网 时间:2024/04/30 12:19

算法结果溢出:

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

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

 

#include <iostream>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 <iostream>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 <iostream>2:3:    using namespace std;4:5:    int main(int argc,char *argv[])6:    {00401250   push        ebp00401251   mov         ebp,esp00401253   sub         esp,48h00401256   push        ebx00401257   push        esi00401258   push        edi00401259   lea         edi,[ebp-48h]0040125C   mov         ecx,12h00401261   mov         eax,0CCCCCCCCh00401266   rep stos    dword ptr [edi]7:        int nVarOne = argc;00401268   mov         eax,dword ptr [ebp+8]0040126B   mov         dword ptr [ebp-4],eax8:        int nVarTwo = argc;0040126E   mov         ecx,dword ptr [ebp+8]00401271   mov         dword ptr [ebp-8],ecx9:10:       nVarTwo = 5 + (nVarOne ++);00401274   mov         edx,dword ptr [ebp-4]00401277   add         edx,50040127A   mov         dword ptr [ebp-8],edx0040127D   mov         eax,dword ptr [ebp-4]00401280   add         eax,100401283   mov         dword ptr [ebp-4],eax11:       nVarTwo = 5 + (++ nVarOne);00401286   mov         ecx,dword ptr [ebp-4]00401289   add         ecx,10040128C   mov         dword ptr [ebp-4],ecx0040128F   mov         edx,dword ptr [ebp-4]00401292   add         edx,500401295   mov         dword ptr [ebp-8],edx12:13:       nVarOne = 5 + (nVarTwo --);00401298   mov         eax,dword ptr [ebp-8]0040129B   add         eax,50040129E   mov         dword ptr [ebp-4],eax004012A1   mov         ecx,dword ptr [ebp-8]004012A4   sub         ecx,1004012A7   mov         dword ptr [ebp-8],ecx14:       nVarOne = 5 + (-- nVarTwo);004012AA   mov         edx,dword ptr [ebp-8]004012AD   sub         edx,1004012B0   mov         dword ptr [ebp-8],edx004012B3   mov         eax,dword ptr [ebp-8]004012B6   add         eax,5004012B9   mov         dword ptr [ebp-4],eax15:16:       return 0;004012BC   xor         eax,eax17:   }004012BE   pop         edi004012BF   pop         esi004012C0   pop         ebx004012C1   mov         esp,ebp004012C3   pop         ebp004012C4   ret


汇编级代码实现我们很清晰的看出自增自减的实现

 

关系运算和逻辑运算:

 

可以利用各种类型的跳转来实现两者间的关系比较,根据比较结果所影响到的标志位来选择对应的条件跳转指令

跳转指令大家都熟悉,不在啰嗦

 

表达式短路&&:

 

源代码:

#include <iostream>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 <iostream>2:3:    using namespace std;4:5:    int Accumulation(int nNumber)6:    {00401250   push        ebp00401251   mov         ebp,esp00401253   sub         esp,40h00401256   push        ebx00401257   push        esi00401258   push        edi00401259   lea         edi,[ebp-40h]0040125C   mov         ecx,10h00401261   mov         eax,0CCCCCCCCh00401266   rep stos    dword ptr [edi]7:        nNumber && (nNumber += Accumulation(nNumber - 1));00401268   cmp         dword ptr [ebp+8],00040126C   je          Accumulation+35h (00401285)0040126E   mov         eax,dword ptr [ebp+8]00401271   sub         eax,100401274   push        eax00401275   call        @ILT+10(Accumulation) (0040100f)0040127A   add         esp,40040127D   mov         ecx,dword ptr [ebp+8]00401280   add         ecx,eax00401282   mov         dword ptr [ebp+8],ecx8:        return nNumber;00401285   mov         eax,dword ptr [ebp+8]9:    }00401288   pop         edi00401289   pop         esi0040128A   pop         ebx0040128B   add         esp,40h0040128E   cmp         ebp,esp00401290   call        __chkesp (004081b0)00401295   mov         esp,ebp00401297   pop         ebp00401298   ret


 

10:11:   int main(int argc,char *argv[])12:   {004012B0   push        ebp004012B1   mov         ebp,esp004012B3   sub         esp,40h004012B6   push        ebx004012B7   push        esi004012B8   push        edi004012B9   lea         edi,[ebp-40h]004012BC   mov         ecx,10h004012C1   mov         eax,0CCCCCCCCh004012C6   rep stos    dword ptr [edi]13:       Accumulation(12);004012C8   push        0Ch004012CA   call        @ILT+10(Accumulation) (0040100f)004012CF   add         esp,414:15:       return 0;004012D2   xor         eax,eax16:   }004012D4   pop         edi004012D5   pop         esi004012D6   pop         ebx004012D7   add         esp,40h004012DA   cmp         ebp,esp004012DC   call        __chkesp (004081b0)004012E1   mov         esp,ebp004012E3   pop         ebp004012E4   ret


我们主要看短路情况:从第一段代码分析可以看出 && 前面的表达式是递归跳出的条件

这正是利用了短路现象,使得代码精炼

 

我们在来看看||运算符的短路情况:

 

源代码基本一样,直接看汇编对照代码:

 

1:    #include <iostream>2:3:    using namespace std;4:5:    int Accumulation(int nNumber)6:    {00401250   push        ebp00401251   mov         ebp,esp00401253   sub         esp,40h00401256   push        ebx00401257   push        esi00401258   push        edi00401259   lea         edi,[ebp-40h]0040125C   mov         ecx,10h00401261   mov         eax,0CCCCCCCCh00401266   rep stos    dword ptr [edi]7:        (nNumber==0) || (nNumber += Accumulation(nNumber - 1));00401268   cmp         dword ptr [ebp+8],00040126C   je          Accumulation+35h (00401285)0040126E   mov         eax,dword ptr [ebp+8]00401271   sub         eax,100401274   push        eax00401275   call        @ILT+10(Accumulation) (0040100f)0040127A   add         esp,40040127D   mov         ecx,dword ptr [ebp+8]00401280   add         ecx,eax00401282   mov         dword ptr [ebp+8],ecx8:        return nNumber;00401285   mov         eax,dword ptr [ebp+8]9:    }00401288   pop         edi00401289   pop         esi0040128A   pop         ebx0040128B   add         esp,40h0040128E   cmp         ebp,esp00401290   call        __chkesp (004081b0)00401295   mov         esp,ebp00401297   pop         ebp00401298   ret


 

10:11:   int main(int argc,char *argv[])12:   {004012B0   push        ebp004012B1   mov         ebp,esp004012B3   sub         esp,40h004012B6   push        ebx004012B7   push        esi004012B8   push        edi004012B9   lea         edi,[ebp-40h]004012BC   mov         ecx,10h004012C1   mov         eax,0CCCCCCCCh004012C6   rep stos    dword ptr [edi]13:       Accumulation(12);004012C8   push        0Ch004012CA   call        @ILT+10(Accumulation) (0040100f)004012CF   add         esp,414:15:       return 0;004012D2   xor         eax,eax16:   }004012D4   pop         edi004012D5   pop         esi004012D6   pop         ebx004012D7   add         esp,40h004012DA   cmp         ebp,esp004012DC   call        __chkesp (004081b0)004012E1   mov         esp,ebp004012E3   pop         ebp004012E4   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;}


汇编代码:

00401678xor eax,eax0040167Acmp dword ptr [ebp+8],50040167Esetne  al        //进行平衡  上面不等于0,设置al,否则为000401681add eax,5

 

但是 setne也只能用在相差为1 的平衡上面,如果大于1,它就无能无力了


方案2:

源代码:

int Condition(int argc,int n){return argc == 5 ? 4 : 10;}


汇编代码:

00401678moveax,dword ptr [ebp+8]0040167Bsub eax,50040167Enegeax00401680sbbeax,eax00401682and eax,600401685addeax,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;}


汇编代码分析:

00401678xor  eax,eax            //清零eax0040167Acmp dword ptr [ebp+8],8      //比较argc和80040167Esetg al              //如果argc>8  al=1,否则为000401681dec eax             //eax减去1,eax现在=0xFFFFFFFF或000401682and  al,0FAh         //eax现在为0xFFFFFFFA(-6补码)或000401685addeax,0Ah          //eax现在为4或10


上面已经解释的很清楚。

但是当表达式2或表达式3中的值为未知数时,就无法使用之前的方法去优化了

编译器会按照常规根据语句流程进行比较和判断

 

方案4:


源代码:

int Condition(int argc,int n){return argc ? 4 : n;}


汇编语言分析:

00401448cmpdword ptr [ebp+8],00040144CjeCondition+27h (00401457)0040144Emov dword ptr [ebp-4],800401455jmpCondition+2Dh (0040145d)00401457mov eax,dword ptr [ebp+0Ch]0040145Amovdword ptr [ebp-4],eax0040145Dmov eax,dword ptr [ebp-4]00401466    ret


位运算

 

<< 左移运算,最高位左移到CF中,最低位补零

>> 右移运算,最高位不变,最低位右移到CF中

 

看代码是如何实现的:

 

源代码:

#include <iostream>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 <iostream>2:3:    using namespace std;4:5:    int BitOperation(int argc)6:    {00401250   push        ebp00401251   mov         ebp,esp00401253   sub         esp,40h00401256   push        ebx00401257   push        esi00401258   push        edi00401259   lea         edi,[ebp-40h]0040125C   mov         ecx,10h00401261   mov         eax,0CCCCCCCCh00401266   rep stos    dword ptr [edi]7:        argc = argc << 3;00401268   mov         eax,dword ptr [ebp+8]0040126B   shl         eax,30040126E   mov         dword ptr [ebp+8],eax8:        argc = argc >> 5;00401271   mov         ecx,dword ptr [ebp+8]00401274   sar         ecx,500401277   mov         dword ptr [ebp+8],ecx9:        argc = argc | 0xFFFF0000;0040127A   mov         edx,dword ptr [ebp+8]0040127D   or          edx,0FFFF0000h00401283   mov         dword ptr [ebp+8],edx10:       argc = argc & 0x0000FFFF;00401286   mov         eax,dword ptr [ebp+8]00401289   and         eax,0FFFFh0040128E   mov         dword ptr [ebp+8],eax11:       argc = argc ^ 0xFFFF0000;00401291   mov         ecx,dword ptr [ebp+8]00401294   xor         ecx,0FFFF0000h0040129A   mov         dword ptr [ebp+8],ecx12:       argc = ~argc;0040129D   mov         edx,dword ptr [ebp+8]004012A0   not         edx004012A2   mov         dword ptr [ebp+8],edx13:14:       return argc;004012A5   mov         eax,dword ptr [ebp+8]15:   }004012A8   pop         edi004012A9   pop         esi004012AA   pop         ebx004012AB   mov         esp,ebp004012AD   pop         ebp004012AE   ret


 

16:17:   int main()18:   {004012D0   push        ebp004012D1   mov         ebp,esp004012D3   sub         esp,44h004012D6   push        ebx004012D7   push        esi004012D8   push        edi004012D9   lea         edi,[ebp-44h]004012DC   mov         ecx,11h004012E1   mov         eax,0CCCCCCCCh004012E6   rep stos    dword ptr [edi]19:       int a = 5;004012E8   mov         dword ptr [ebp-4],520:       BitOperation(a);004012EF   mov         eax,dword ptr [ebp-4]004012F2   push        eax004012F3   call        @ILT+10(BitOperation) (0040100f)004012F8   add         esp,421:22:       return 0;004012FB   xor         eax,eax23:   }004012FD   pop         edi004012FE   pop         esi004012FF   pop         ebx00401300   add         esp,44h00401303   cmp         ebp,esp00401305   call        __chkesp (004081d0)0040130A   mov         esp,ebp0040130C   pop         ebp0040130D   ret


上面的反汇编代码比较简单,是有符号的位运算

下面无符号的还有点不一样:

 

源代码:

#include <iostream>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 <iostream>2:3:    using namespace std;4:5:    void BitOperation(int argc)6:    {00401250   push        ebp00401251   mov         ebp,esp00401253   sub         esp,44h00401256   push        ebx00401257   push        esi00401258   push        edi00401259   lea         edi,[ebp-44h]0040125C   mov         ecx,11h00401261   mov         eax,0CCCCCCCCh00401266   rep stos    dword ptr [edi]7:        unsigned int nVar = argc;00401268   mov         eax,dword ptr [ebp+8]0040126B   mov         dword ptr [ebp-4],eax8:        nVar <<= 3;0040126E   mov         ecx,dword ptr [ebp-4]00401271   shl         ecx,300401274   mov         dword ptr [ebp-4],ecx9:        nVar >>= 5;00401277   mov         edx,dword ptr [ebp-4]0040127A   shr         edx,50040127D   mov         dword ptr [ebp-4],edx10:11:   }00401280   pop         edi00401281   pop         esi00401282   pop         ebx00401283   mov         esp,ebp00401285   pop         ebp00401286   ret


 

12:13:   int main()14:   {004012A0   push        ebp004012A1   mov         ebp,esp004012A3   sub         esp,44h004012A6   push        ebx004012A7   push        esi004012A8   push        edi004012A9   lea         edi,[ebp-44h]004012AC   mov         ecx,11h004012B1   mov         eax,0CCCCCCCCh004012B6   rep stos    dword ptr [edi]15:       int a = 5;004012B8   mov         dword ptr [ebp-4],516:       BitOperation(a);004012BF   mov         eax,dword ptr [ebp-4]004012C2   push        eax004012C3   call        @ILT+10(BitOperation) (0040100f)004012C8   add         esp,417:18:       return 0;004012CB   xor         eax,eax19:   }004012CD   pop         edi004012CE   pop         esi004012CF   pop         ebx004012D0   add         esp,44h004012D3   cmp         ebp,esp004012D5   call        __chkesp (004081a0)004012DA   mov         esp,ebp004012DC   pop         ebp004012DD   ret


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击