C++反汇编学习笔记(四)
Chinese:
1、加法指令是add,执行加法运算时,针对不同的操作数,转换的指令也会不同。
2、Debug编译产生的汇编代码是和源码一一对应的,主要建立在调试的基础上做一些优化。而Release编译则是做了更多的优化,为了达到最快的执行效率。
3、常用的两种优化方案: 常量传播 和 常量折叠
(1) 常量传播
将编译期间可计算出结果的变量转换成常量这样就减少了变量的使用
void main() { int nVar = 1; printf("%d", nVar); } // 优化后 void main() { printf("%d", 1); }
(2) 常量折叠
当计算公式中出现多个常量进行计算的情况时,且编译器可以在编译期间计算出结果时,这样源码中的所有的常量计算都会被计算结果代替。
void main() { int nVar = 1 + 5 - 3 * 6; printf("%d", nVar); } // 优化后 void main() { printf("%d", -12); // 先常量折叠,再常量传播 }
4、减法指令使用的是sub,计算机是通过补码的形式将减法转换成加法的。
5、乘法指令分为有符号imul和无符号mul两种。由于乘法指令的执行周期较长,在编译过程中,编译器会先尝试将乘法转换成加法,或使用移位等周期较短的指令。
6、除法指令分为有符号idiv和无符号div两种。除法指令的执行周期较长,效率也较低。C++中,除法运算不保留余数,有专门求余数的运算符,对于整数除法,结果仅仅保留整数部分。除法的优化非常复杂,这里便略去,有兴趣的可以参考相关资料。
7、表达式短路
表达式短路通过逻辑与运算和逻辑或运算使语句根据条件在执行时发生中断,从而不予执行后面的语句。
逻辑与运算:
// C++ Code int Accumulation(int nNumber) { // 当nNumber等于0时,逻辑与运算符左边的值为假,将不会执行右边语句 // 形成表达式短路,从而找到递归出口 nNumber && (nNumber += Accumulation(nNumber - 1)); return nNumber; } ;短路模式汇编代码 cmp dword ptr [ebp+8], 0 je Accumulation+35h (0040bac5) ;跳转失败 进入递归调用 mov eax, dword ptr [ebp+8] ;对变量nNumber减1后,结果作为参数压栈 sub eax, 1 push eax ;继续调用自己 形成递归 call @ILT+30 (Accumulation) (00401023) add esp, 4 mov ecx, dword ptr [ebp+8] add ecx, eax mov dword ptr [ebp+8], ecx ;返回结果 mov eax, dword ptr [ebp+8]
逻辑或运算:
// C++ Code int Accumulation(int nNumber) { nNumber == 0 || nNumber += Accumulation(nNumber - 1)); return nNumber; } ;使用逻辑或运算造成的表达式短路 cmp dword ptr [ebp+8], 0 je Accumulation+35h (00401635) mov eax, dword ptr [ebp+8] sub eax, 1 push eax call @ILT+30 (Accumulation) (00401023) add esp, 4 mov ecx, dword ptr [ebp+8] add ecx, eax mov dword ptr [ebp+8], ecx ;返回 mov eax, dword ptr [ebp+8]
虽然使用的逻辑运算符不同,但是两种情况下,左边的语句块都是和0比较,因此生成了相同的汇编代码。