你的位置:首页 > ASP.net教程

[ASP.net教程]C++反汇编学习笔记(五)各种算数运算的工作形式(2)


1、  除法

  对计算机而言,整数除法的结果必须是整数。计算机取整数部分的方式有如下几种:

  ①    向下取整

    如:-3.5=>-4;3.5=>3;

  ②    向上取整

    如:-3.5=>-3;3.5=>4;

  ③    向零取整

    如:-3.5=>-3;3.5=>3;

  C++和大多数高级语言,对整数除法都规定向零取整。

         整数除法的几种情况:

    ①    常量除以常量

    ②    变量除以常量(常量值为2的幂)

    ③    变量除以常量(常量值为非2的幂)

    ④    变量除以常量(常量值为负的2的幂)

    ⑤    变量除以常量(常量值为负的非2的幂)

    ⑥    变量除以变量

  //③和⑤未在代码中进行详细分析,因为有点复杂,搞不太懂优化规则。看最后的总结

C++原码

Debug版

Release版

#include <iostream>

using namespace std;

int main()

{

         int a, b;

         cin >> a >> b;

         //变量除以常量(常量值为2的幂)

         a = a / 2;

         cout << a ;

         a = a / 8;

         cout << a;

         //变量除以常量(常量值为负的2的幂)

         a = a / -2;

         cout << a;

         a = a / -8;

         cout << a;

         //变量除以常量(常量值为非2的幂)

         a = a / 15;

         cout << a;

         //

         a = a / 11;

         cout << a;

         //变量除以常量(常量值为负的非2的幂)

         a = a / -15;

         cout << a;

         //

         a = a / -11;

         cout << a;

         //常量除以常量

         a = 18 / 15;

         cout << a;

         //变量除以变量

         a = a / b;

         cout << a;

 

 

         system("pause");

         return 0;

}

#include <iostream>

using namespace std;

int main()

{

.....略

         int a, b;

         cin >> a >> b;

..........略

         //变量除以常量(常量值为2的幂)

         a = a / 2;

00F75EB6  mov         eax,dword ptr [a] 

00F75EB9  cdq 

00F75EBA  sub         eax,edx 

00F75EBC  sar         eax,1 

00F75EBE  mov         dword ptr [a],eax 

         cout << a ;

00F75EC1  mov         esi,esp 

         cout << a ;

00F75EC3  mov         eax,dword ptr [a] 

00F75EC6  push        eax 

00F75EC7  mov         ecx,dword ptr ds:[0F810A8h] 

00F75ECD  call        dword ptr ds:[0F81094h] 

00F75ED3  cmp         esi,esp 

00F75ED5  call        __RTC_CheckEsp (0F71339h) 

         a = a / 8;

00F75EDA  mov         eax,dword ptr [a] 

00F75EDD  cdq 

00F75EDE  and         edx,7 

00F75EE1  add         eax,edx 

00F75EE3  sar         eax,3 

00F75EE6  mov         dword ptr [a],eax 

         cout << a;

00F75EE9  mov         esi,esp 

00F75EEB  mov         eax,dword ptr [a] 

00F75EEE  push        eax 

00F75EEF  mov         ecx,dword ptr ds:[0F810A8h] 

00F75EF5  call        dword ptr ds:[0F81094h] 

00F75EFB  cmp         esi,esp 

00F75EFD  call        __RTC_CheckEsp (0F71339h) 

         //变量除以常量(常量值为负的2的幂)

         a = a / -2;

00F75F02  mov         eax,dword ptr [a] 

00F75F05  cdq 

00F75F06  sub         eax,edx 

00F75F08  sar         eax,1 

00F75F0A  neg         eax 

00F75F0C  mov         dword ptr [a],eax 

         cout << a;

00F75F0F  mov         esi,esp 

00F75F11  mov         eax,dword ptr [a] 

00F75F14  push        eax 

00F75F15  mov         ecx,dword ptr ds:[0F810A8h] 

         cout << a;

00F75F1B  call        dword ptr ds:[0F81094h] 

00F75F21  cmp         esi,esp 

00F75F23  call        __RTC_CheckEsp (0F71339h) 

         a = a / -8;

00F75F28  mov         eax,dword ptr [a] 

00F75F2B  cdq 

00F75F2C  and         edx,7 

00F75F2F  add         eax,edx 

00F75F31  sar         eax,3 

00F75F34  neg         eax 

00F75F36  mov         dword ptr [a],eax 

         cout << a;

00F75F39  mov         esi,esp 

00F75F3B  mov         eax,dword ptr [a] 

00F75F3E  push        eax 

00F75F3F  mov         ecx,dword ptr ds:[0F810A8h] 

00F75F45  call        dword ptr ds:[0F81094h] 

00F75F4B  cmp         esi,esp 

00F75F4D  call        __RTC_CheckEsp (0F71339h) 

         //变量除以常量(常量值为非2的幂)

         a = a / 15;

00F75F52  mov         eax,dword ptr [a] 

00F75F55  cdq 

00F75F56  mov         ecx,0Fh 

00F75F5B  idiv        eax,ecx 

00F75F5D  mov         dword ptr [a],eax 

         cout << a;

00F75F60  mov         esi,esp 

00F75F62  mov         eax,dword ptr [a] 

00F75F65  push        eax 

00F75F66  mov         ecx,dword ptr ds:[0F810A8h] 

00F75F6C  call        dword ptr ds:[0F81094h] 

00F75F72  cmp         esi,esp 

00F75F74  call        __RTC_CheckEsp (0F71339h) 

         //

         a = a / 11;

00F75F79  mov         eax,dword ptr [a] 

00F75F7C  cdq 

00F75F7D  mov         ecx,0Bh 

00F75F82  idiv        eax,ecx 

00F75F84  mov         dword ptr [a],eax 

         cout << a;

00F75F87  mov         esi,esp 

00F75F89  mov         eax,dword ptr [a] 

00F75F8C  push        eax 

00F75F8D  mov         ecx,dword ptr ds:[0F810A8h] 

00F75F93  call        dword ptr ds:[0F81094h] 

00F75F99  cmp         esi,esp 

00F75F9B  call        __RTC_CheckEsp (0F71339h) 

         //变量除以常量(常量值为负的非2的幂)

         a = a / -15;

00F75FA0  mov         eax,dword ptr [a] 

00F75FA3  cdq 

00F75FA4  mov         ecx,0FFFFFFF1h 

00F75FA9  idiv        eax,ecx 

00F75FAB  mov         dword ptr [a],eax 

         cout << a;

00F75FAE  mov         esi,esp 

00F75FB0  mov         eax,dword ptr [a] 

00F75FB3  push        eax 

00F75FB4  mov         ecx,dword ptr ds:[0F810A8h] 

00F75FBA  call        dword ptr ds:[0F81094h] 

00F75FC0  cmp         esi,esp 

00F75FC2  call        __RTC_CheckEsp (0F71339h) 

         //

         a = a / -11;

00F75FC7  mov         eax,dword ptr [a] 

00F75FCA  cdq 

00F75FCB  mov         ecx,0FFFFFFF5h 

00F75FD0  idiv        eax,ecx 

00F75FD2  mov         dword ptr [a],eax 

         cout << a;

00F75FD5  mov         esi,esp 

00F75FD7  mov         eax,dword ptr [a] 

00F75FDA  push        eax 

00F75FDB  mov         ecx,dword ptr ds:[0F810A8h] 

00F75FE1  call        dword ptr ds:[0F81094h] 

00F75FE7  cmp         esi,esp 

00F75FE9  call        __RTC_CheckEsp (0F71339h) 

         //常量除以常量

         a = 18 / 15;

00F75FEE  mov         dword ptr [a],1 

         cout << a;

00F75FF5  mov         esi,esp 

00F75FF7  mov         eax,dword ptr [a] 

00F75FFA  push        eax 

00F75FFB  mov         ecx,dword ptr ds:[0F810A8h] 

00F76001  call        dword ptr ds:[0F81094h] 

00F76007  cmp         esi,esp 

00F76009  call        __RTC_CheckEsp (0F71339h) 

         //变量除以变量

         a = a / b;

00F7600E  mov         eax,dword ptr [a] 

00F76011  cdq 

00F76012  idiv        eax,dword ptr [b] 

00F76015  mov         dword ptr [a],eax 

         cout << a;

00F76018  mov         esi,esp 

00F7601A  mov         eax,dword ptr [a] 

00F7601D  push        eax 

00F7601E  mov         ecx,dword ptr ds:[0F810A8h] 

00F76024  call        dword ptr ds:[0F81094h] 

00F7602A  cmp         esi,esp 

00F7602C  call        __RTC_CheckEsp (0F71339h) 

 

 

         system("pause");

............略

         return 0;

00F76048  xor         eax,eax 

}

...........略

#include <iostream>

using namespace std;

int main()

{

//...........略

         int a, b;

         cin >> a >> b;

011712B0  mov         ecx,dword ptr ds:[1173038h] 

         int a, b;

         cin >> a >> b;

//................略

         //变量除以常量(常量值为2的幂)

         a = a / 2;

011712CC  mov         eax,dword ptr [a] 

         cout << a ;

011712CF  mov         ecx,dword ptr ds:[117303Ch] 

//用eax的符号位值填充edx,即如果eax(变量a)的值是负数,则edx=0XFFFFFFFF=-1,否则edx=0x0=0;

011712D5  cdq 

//因为有符号除法的规则是向0取整,所以如果变量a是负数,则a+1(eax-edx=a-(-1)=a+1),否则a+0(eax-edx=a-0)不做处理,最后进行左移。

//例如:a=5(0x00000101),右移1位(a/2)得a=2;

//a=-5(0x11111011),如果直接右移1位(a/2)得a=0x11111101=-3,而向零取整的正确结果是-2,所以算数移位指令等价与向下取整,因此被除数为负数时,先对其进行调整(a+(2的n次方-1)),再进行移位。

//a+(2的1次方-1)=-5+1=-4(0X11111100),右移1位得a=-2(0X11111110)

011712D6  sub         eax,edx 

011712D8  sar         eax,1 

011712DA  push        eax 

011712DB  mov         dword ptr [a],eax 

011712DE  call        dword ptr ds:[1173024h] 

         a = a / 8;

011712E4  mov         eax,dword ptr [a] 

         cout << a;

011712E7  mov         ecx,dword ptr ds:[117303Ch] 

011712ED  cdq 

//如果a为负,则eax=-1,然后eax&7得eax=7(8=2的3次方,然后减1),然后eax+edx,最后进行移位

//例如:a=-9(0x11110111),如果直接右移3位(除以8)得a=-2(0x11111110),而我们要的结果是-1,所以a+7=-2(0x11111110)再进行右移3位得a=-1

011712EE  and         edx,7

011712F1  add         eax,edx 

011712F3  sar         eax,3 

011712F6  push        eax 

011712F7  mov         dword ptr [a],eax 

011712FA  call        dword ptr ds:[1173024h] 

         //变量除以常量(常量值为负的2的幂)

         a = a / -2;

//和常量值为正的2的幂情况相似。先进行a除以正的2的幂,最后将结果neg取反。

//例如:a=5时,5/-2=-2;首先a/2得2,取反后a=-2;

// a=-5时,-5/-2=2;首先a/2得-2,取反后a=2;

01171300  mov         eax,dword ptr [a] 

         cout << a;

01171303  mov         ecx,dword ptr ds:[117303Ch] 

01171309  cdq 

0117130A  sub         eax,edx 

0117130C  sar         eax,1 

         cout << a;

0117130E  neg         eax 

01171310  push        eax 

01171311  mov         dword ptr [a],eax 

01171314  call        dword ptr ds:[1173024h] 

         a = a / -8;

0117131A  mov         eax,dword ptr [a] 

         cout << a;

0117131D  mov         ecx,dword ptr ds:[117303Ch] 

01171323  cdq 

01171324  and         edx,7 

01171327  add         eax,edx 

01171329  sar         eax,3 

0117132C  neg         eax 

0117132E  push        eax 

0117132F  mov         dword ptr [a],eax 

01171332  call        dword ptr ds:[1173024h] 

         //变量除以常量(常量值为非2的幂)

         a = a / 15;

         cout << a;

//由于除法指令的周期比乘法指令周期长很多,所以编译器会用周期较短的乘法和其他指令代替除法。

//设被除数变量为a,b为除数常量,则:

// =a*=a*=a**,由于b为常量,且的取值由编译器选择,所以的//值在编译期间就可以计算出来。在vc中,n的取值都大于等于32,这样就可//以直接调整使用乘法结果的高位。这个值常被称为魔数或幻数。

01171338  mov         ecx,dword ptr ds:[117303Ch] 

0117133E  mov         eax,88888889h  //魔数

01171343  imul        dword ptr [a] 

01171346  add         edx,dword ptr [a] 

01171349  sar         edx,3 

0117134C  mov         eax,edx 

0117134E  shr         eax,1Fh 

01171351  add         eax,edx 

01171353  push        eax 

01171354  mov         dword ptr [a],eax 

01171357  call        dword ptr ds:[1173024h] 

         //

         a = a / 11;

         cout << a;

0117135D  mov         ecx,dword ptr ds:[117303Ch] 

01171363  mov         eax,2E8BA2E9h  //魔数

01171368  imul        dword ptr [a] 

0117136B  sar         edx,1 

0117136D  mov         eax,edx 

0117136F  shr         eax,1Fh 

01171372  add         eax,edx 

01171374  push        eax 

01171375  mov         dword ptr [a],eax 

01171378  call        dword ptr ds:[1173024h] 

         //变量除以常量(常量值为负的非2的幂)

         a = a / -15;

         cout << a;

0099137E  mov         ecx,dword ptr ds:[99303Ch] 

00991384  mov         eax,77777777h 

00991389  imul        dword ptr [a] 

0099138C  sub         edx,dword ptr [a] 

0099138F  sar         edx,3 

00991392  mov         eax,edx 

00991394  shr         eax,1Fh 

00991397  add         eax,edx 

00991399  push        eax 

0099139A  mov         dword ptr [a],eax 

0099139D  call        dword ptr ds:[993024h] 

         //

         a = a / -11;

009913A3  mov         eax,0D1745D17h 

009913A8  imul        dword ptr [a] 

009913AB  sar         edx,1 

009913AD  mov         eax,edx 

009913AF  shr         eax,1Fh 

         cout << a;

009913B2  mov         ecx,dword ptr ds:[99303Ch] 

009913B8  add         eax,edx 

009913BA  push        eax 

009913BB  mov         dword ptr [a],eax 

009913BE  call        dword ptr ds:[993024h] 

//常量除以常量

         a = 18 / 15;

         cout << a;

//编辑期间直接计算出结果

01171393  mov         ecx,dword ptr ds:[117303Ch] 

01171399  push        1 

0117139B  mov         dword ptr [a],1 

011713A2  call        dword ptr ds:[1173024h] 

         //变量除以变量

         a = a / b;

//变量a和b的值不确定,所以编译期间不能进行优化,而直接使用idiv除法指令

011713A8  mov         eax,dword ptr [a] 

011713AB  cdq 

011713AC  idiv        eax,dword ptr [b] 

         cout << a;

011713AF  mov         ecx,dword ptr ds:[117303Ch] 

011713B5  mov         dword ptr [a],eax 

011713B8  push        eax 

011713B9  call        dword ptr ds:[1173024h] 

 

 

         system("pause");

011713BF  push        117319Ch 

011713C4  call        dword ptr ds:[11730C4h] 

         return 0;

}

011713CA  mov         ecx,dword ptr [ebp-4] 

         return 0;

}

...............略

 

 

 

                                                                                                                 除数为非2的幂总结:

mov eax,魔数

imul ......

sar edx,......

mov reg,edx

shr reg,1fh

add edx,reg

;此后直接使用edx的值,eax弃而不用

当遇到左边指令序列时,基本可以判定是除法优化后的代码,其除法原型为变量a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a。接下来统计右移的总次数以确定公式中的n值i,然后使用公式d=,将魔数代入公式求解常量除数b的近似值,四舍五入取整后,即可恢复除法原型。

mov eax,魔数

;这里的reg表示通用寄存器

mul reg

sub reg,edx

shr reg,1

add reg,edx

shr reg,A;这句或许没有,如果没有,则n值为1,否则这里的A就是n-1的值。

;此后直接使用reg的值,eax弃而不用。

如果遇到左边的指令序列,基本可以判定是除法优化后的代码。其除法原型为a除以常量b,mul表明是无符号计算,其操作数是优化前的被除数a,接下来统计右移的总次数以确定公式中的n值,然后使用公式b=将魔数代入公式求解常量除数b,即可恢复除法原型。

mov eax,魔数(大于7FFFFFFFH)

imul reg

add edx,reg

sar edx,......

mov reg, edx

shr reg,1fh

add edx,reg

;此后直接使用edx的值。

当遇到左边的指令序列时,基本可以断定是除法优化后的代码,其除法原型为a除以常量b,imul表明是有符号计算,其操作数是优化前的被除数a,接下来统计右移的总次数以确定公式中n的值,然后使用公式b=,将魔数代入公式求解常量除数b,即可恢复除法原型。

                                                                                                                  除数为负的非2的幂总结

mov eax,魔数(大于7FFFFFFFH)

imul reg

sar edx,......

mov reg,edx

shr reg,1FH

add edx,reg

;此后直接使用edx的值

如果遇到左边的指令序列,基本可以判定是除法优化后的代码,其除法原型为a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a,由于魔数取值小于等于7FFFFFFFH,而imul和sar之间有sub指令来调整乘积,故可认定除数为负,且魔数为补码形式。接下来统计右移的总次数以确定公式中n的值,然后使用公式|b|=,将魔数代入公式求解常量除数|b|,即可恢复除法原型。

mov eax,魔数(小于等于7FFFFFFFH)

imul reg

sub edx,reg

sar edx,......

mov reg,edx

shr reg,1FH

add edx,reg

;此后直接使用edx的值

如果遇到左边的指令序列,基本可以判定是除法优化后的代码,其除法原型为a除以常量b,imul可表明是有符号计算,其操作数是优化前的被除数a,由于魔数取值大于7FFFFFFFH,而imul和sar之间未见任何调整代码,故可认定除数为负,且魔数为补码形式。接下来统计右移的总次数以确定公式中n的值,然后使用公式|b|=,将魔数代入公式求解常量除数|b|,即可恢复除法原型。