算术逻辑位运算
来源:互联网 发布: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
- 算术逻辑位运算
- 【16位汇编】neg+sbb算术运算代替逻辑跳转
- 13_运算符_01_算术_逻辑_位运算符_扩展运算符
- Java运算符_算术_逻辑_位运算符_扩展运算符
- JS程序设计基础:变量,算术、关系、赋值、复合赋值、逻辑、位运算符与表达式
- java中的运算符|基础 学习笔记 算术、赋值、比较、逻辑、位
- java学习之旅13--运算符_01_算术_逻辑_位运算符_扩展运算符
- Java位算术运算符
- 逻辑位运算
- 逻辑位运算示例
- 黑马程序员_04_算术_转移_逻辑_位_三元_运算符_判断语句
- 黑马程序员-----Java基础知识---常量---进制----变量---运算符(算术--赋值--比较--逻辑--位--三元)----if语句---switch语句
- 位运算,有符号值, 算术移位
- 算术运算2(逻辑计算和复合运算)
- (21)算术运算符、逻辑和关系运算符
- 逻辑代数与位运算
- 逻辑位运算小结5
- 逻辑位运算小结6
- Jboss4中使用配置发送邮件
- 绝对不容错过的野生动物wildlife摄影作品
- 理解RESTful架构
- 电视收视率评估工具推荐
- Android SDK 无法更新的解决办法
- 算术逻辑位运算
- Windows 8 Metro中对GridView或ListView使用分组功能详解
- sql脚本
- Struts2教程1:第一个Struts2程序
- NDK 编译中so的名字不要用下划线哦
- hoho
- Android Camera 使用小结
- QML与Qt C++ 交互机制探讨与总结
- getaddrinfo()函数详解