C++反汇编


                                                            

 

 

 

 

 

 

Microsoft  Visual C++ 6.0在使用浮点数前,需要先对浮点寄存器进行初始化,然后才能使用

 

程序崩溃的原因:在浮点寄存器没有初始化时使用浮点操作,将无法转换小数部分

在代码中任意一个位置(用之前)定义一个浮点类型变量即可对浮点寄存器初始化

 

在C++中,引用和指针没有什么区别。只是引用是通过编译器实现寻址,而指针需要手动寻址。指针虽然灵活,但操作失误将产生严重的后果,而使用引用则不存在这种问题

 

 

 

 

 

mainCRTStartup函数:

 1 #ifdef WPRFLAG  2 void wmainCRTStartup(  3 #else  /* WPRFLAG */
 4 void mainCRTStartup(  5 #endif  /* WPRFLAG */
 6  
 7 #endif  /* _WINMAIN_ */
 8         void
 9  )  10  
 11 {  12         int mainret;  13  
 14 #ifdef _WINMAIN_  15         _TUCHAR *lpszCommandLine;  16  STARTUPINFO StartupInfo;  17 #endif  /* _WINMAIN_ */
 18  
 19         /*
 20  * Get the full Win32 version  21          */
 22         _osver = GetVersion();  23  
 24         _winminor = (_osver >> 8) & 0x00FF ;  25         _winmajor = _osver & 0x00FF ;  26         _winver = (_winmajor << 8) + _winminor;  27         _osver = (_osver >> 16) & 0x00FFFF ;  28  
 29 #ifdef _MT  30         if ( !_heap_init(1) )               /* initialize heap */
 31 #else  /* _MT */
 32         if ( !_heap_init(0) )               /* initialize heap */
 33 #endif  /* _MT */
 34             fast_error_exit(_RT_HEAPINIT);  /* write message and die */
 35  
 36 #ifdef _MT  37         if( !_mtinit() )                    /* initialize multi-thread */
 38             fast_error_exit(_RT_THREAD);    /* write message and die */
 39 #endif  /* _MT */
 40  
 41         /*
 42  * Guard the remainder of the initialization code and the call  43  * to user's main, or WinMain, function in a __try/__except  44  * statement.  45          */
 46  
 47  __try {  48  
 49             _ioinit();                      /* initialize lowio */
 50  
 51 #ifdef WPRFLAG  52             /* get wide cmd line info */
 53             _wcmdln = (wchar_t *)__crtGetCommandLineW();  54  
 55             /* get wide environ info */
 56             _wenvptr = (wchar_t *)__crtGetEnvironmentStringsW();  57  
 58  _wsetargv();  59  _wsetenvp();  60 #else  /* WPRFLAG */
 61             /* get cmd line info */
 62             _acmdln = (char *)GetCommandLineA();  63  
 64             /* get environ info */
 65             _aenvptr = (char *)__crtGetEnvironmentStringsA();  66  
 67  _setargv();  68  _setenvp();  69 #endif  /* WPRFLAG */
 70  
 71             _cinit();                       /* do C data initialize */
 72  
 73 #ifdef _WINMAIN_  74  
 75             StartupInfo.dwFlags = 0;  76             GetStartupInfo( &StartupInfo );  77  
 78 #ifdef WPRFLAG  79             lpszCommandLine = _wwincmdln();  80             mainret = wWinMain(  81 #else  /* WPRFLAG */
 82             lpszCommandLine = _wincmdln();  83             mainret = WinMain(  84 #endif  /* WPRFLAG */
 85  GetModuleHandleA(NULL),  86  NULL,  87  lpszCommandLine,  88                                StartupInfo.dwFlags & STARTF_USESHOWWINDOW  89                                     ? StartupInfo.wShowWindow  90  : SW_SHOWDEFAULT  91  );  92 #else  /* _WINMAIN_ */
 93  
 94 #ifdef WPRFLAG  95             __winitenv = _wenviron;  96             mainret = wmain(__argc, __wargv, _wenviron);  97 #else  /* WPRFLAG */
 98             __initenv = _environ;  99             mainret = main(__argc, __argv, _environ); 100 #endif  /* WPRFLAG */
101  
102 #endif  /* _WINMAIN_ */
103  exit(mainret); 104  } 105  __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) ) 106  { 107             /*
108  * Should never reach here 109              */
110  _exit( GetExceptionCode() ); 111  
112         } /* end of try - except */
113  
114 }

 

在VC++ 6.0中main函数被调用前要先调用函数如下:

1,GetVersion()

2,_heap_init()

3,GetCommandLineA();

4,_crtGetEnvironmentStringsA()

5,_setargv()

6,_setenvp()

7,_cinit()

 这些函数调用结束后就会调用main函数:

 

VC++6.0中,算数运算与其他传递计算结果的代码组合才能被视为一条有效的语句。

单独的算术运算虽然可以编译通过,但是并不会生成代码。

因为只进行计算而没有传递结果的运算不会对程序结果有任何影响,此时编译器将其视为无效语句:

 

VC++6.0中常用的优化方案有两种:

O1方案:生成文件占用空间最小

O2方案:执行效率最快

Release 使用的是O2方案

Debug 使用的是Od+ZI选项,加有调试内容

 ->Debug版反汇编:

 1 1: #include <stdio.h>  2 2:  3 3:    int main()  4 4: {  5 0040D400   push ebp  6 0040D401   mov ebp,esp  7 0040D403   sub esp,48h  8 0040D406   push ebx  9 0040D407   push esi 10 0040D408   push edi 11 0040D409   lea edi,[ebp-48h] 12 0040D40C   mov ecx,12h 13 0040D411   mov eax,0CCCCCCCCh 14 0040D416   rep stos dword ptr [edi] 15 5:        15 + 20; //无效语句 16 6: 17 7:        int nVarOne = 0 ; 18 0040D418   mov         dword ptr [ebp-4],0 ;将立即数0传入到地址ebp-4中,即变量nVarOne所在的地址
19 8:        int nVarTwo = 0 ; 20 0040D41F   mov         dword ptr [ebp-8],0 ;将立即数0传入到地址ebp-8中,即变量nVarTwo所在的地址
21 9: 22 10:       nVarOne = nVarOne + 1; 23 0040D426   mov         eax,dword ptr [ebp-4] ;取出nVarOne,放入eax寄存器中 24 0040D429   add         eax,1 ;把eax中数据(nVarOne)加1
25 0040D42C   mov         dword ptr [ebp-4],eax ;把eax中的值放回ebp-4地址处 26 11: 27 12: 28 13:       nVarOne = 1 + 2; 29 0040D42F   mov         dword ptr [ebp-4],3 ;把3放入ebp-4的地址处,即nVarOne所在地址
30 14: 31 15:       nVarOne = nVarOne + nVarTwo; 32 0040D436   mov         ecx,dword ptr [ebp-4] ;把nVarOne取出,放在寄存器ecx中 33 0040D439   add         ecx,dword ptr [ebp-8] ;ecx中的值(nVarOne) 加上 ebp-8 中的值(nVarTwo),结果放在ecx中 34 0040D43C   mov         dword ptr [ebp-4],ecx ;把加后的ecx的值放回ebp-4地址处,即nVarone地址处 35 16: 36 17:       printf("nVarOnw = %d \r\n",nVarOne); 37 0040D43F   mov         edx,dword ptr [ebp-4] ;把nVarOne放入edx中 38 0040D442   push edx ;把edx入栈 39 0040D443   push        offset string "nVarOnw = %d \r\n" (00422e80) ;把格式控制字符串入栈 40 0040D448   call        printf (0040d6d0) ;调用printf函数 41 0040D44D   add         esp,8 ;主调函数修复栈
42 18: 43 19:       return 0; 44 0040D450   xor eax,eax ;清零eax 45 20: } 46 0040D452   pop edi ;edi出栈 47 0040D453   pop esi ;esi出栈 48 0040D454   pop ebx ;ebx出栈 49 0040D455   add esp,48h ;main函数修复栈 50 0040D458   cmp ebp,esp ;比较ebp和esp 51 0040D45A   call        __chkesp (0040d690) ;调用__chkesp检查调整栈 52 0040D45F   mov esp,ebp ;恢复esp栈 53 0040D461   pop ebp ;ebp出栈 54 0040D462   ret ;返回

 ->Release版反汇编:

1 ;int __cdecl main(int argc,const char **argv,const char **envp)
2 _main proc near 3 push 3
4 push offset Format    ; "nVarOne = %d \r\n"
5 call _printf 6 add  esp,8
7 xor eax,eax 8 retn
9 _main endp

 

在编译过程中,编译器常常会采用“常量传播”和“常量折叠”这样的方案对代码中的变量和常量进行优化:

>常量传播:将编译期间可计算 出结果的变量转化为常量,这样就减少了变量的使用

例如,这样一段代码:

1 #include <stdio.h>
2 
3 int main() 4 { 5     int nVar = 1; 6     printf("nVar = %d \r\n",nVar); 7     return 0; 8 }

被优化后为:

1 #include <stdio.h>
2 
3 int main() 4 { 5     printf("nVar = %d \r\n",1); 6     return 0; 7 }

 

>常量折叠:当计算公式中出现多个常量进行计算的情况时,且编译器可以在编译期间计算出结果时,这样所有常量计算都被计算结果代替:

这样一段代码:

1 #include <stdio.h>
2 
3 int main() 4 { 5     int nVar = 1 + 5 - 3 * 6; 6     printf("nVar = %d \r\n",nVar); 7     return 0; 8 }

常量折叠后:

1 #include <stdio.h>
2 
3 int main() 4 { 5     int nVar = -12; 6     printf("nVar = %d \r\n",nVar); 7     return 0; 8 }

再经常量传播后:

1 #include <stdio.h>
2 
3 int main() 4 { 5     printf("nVar = %d \r\n",-12); 6     return 0; 7 }

 

如果开启O2优化方案后,变量在程序的逻辑中,声明的变量没有被修改过,而且上下文中不存在针对变量取地址和间接访问的操作,那么这个变量就等价于常量
编译器就认为可以删除这个变量,直接用常量代替

看下面另一个例子:

源代码:

 1 #include <stdio.h>
 2 
 3 int main(int argc,char *argv[])  4 {  5     int nVarOne = argc;  6     int nVarTwo = argc;  7 
 8     nVarOne = nVarOne + 1;  9     nVarOne = 1 + 2; 10     nVarOne = nVarOne + nVarTwo; 11 
12     printf("nVarOne = %d \r\n",nVarOne); 13 
14     return 0; 15 }

优化后源代码可能为:

1 #include <stdio.h>
2 
3 int main(int argc,char *argv[]) 4 { 5     printf("nVarOne = %d \r\n",3 + argc); 6     return 0; 7 }

 

汇编代码:Debug版

 1 1: #include <stdio.h>  2 2:  3 3:    int main(int argc,char *argv[])  4 4: {  5 0040D400   push ebp  6 0040D401   mov ebp,esp  7 0040D403   sub esp,48h  8 0040D406   push ebx  9 0040D407   push esi 10 0040D408   push edi 11 0040D409   lea edi,[ebp-48h] 12 0040D40C   mov ecx,12h 13 0040D411   mov eax,0CCCCCCCCh 14 0040D416   rep stos dword ptr [edi] 15 5:        int nVarOne = argc; 16 0040D418   mov         eax,dword ptr [ebp+8] 17 0040D41B   mov         dword ptr [ebp-4],eax 18 6:        int nVarTwo = argc; 19 0040D41E   mov         ecx,dword ptr [ebp+8] 20 0040D421   mov         dword ptr [ebp-8],ecx 21 7: 22 8:        nVarOne = nVarOne + 1; 23 0040D424   mov         edx,dword ptr [ebp-4] 24 0040D427   add         edx,1
25 0040D42A   mov         dword ptr [ebp-4],edx 26 9:        nVarOne = 1 + 2; 27 0040D42D   mov         dword ptr [ebp-4],3
28 10:       nVarOne = nVarOne + nVarTwo; 29 0040D434   mov         eax,dword ptr [ebp-4] 30 0040D437   add         eax,dword ptr [ebp-8] 31 0040D43A   mov         dword ptr [ebp-4],eax 32 11: 33 12:       printf("nVarOne = %d \r\n",nVarOne); 34 0040D43D   mov         ecx,dword ptr [ebp-4] 35 0040D440   push ecx 36 0040D441   push        offset string "nVarOne = %d \r\n" (00422e80) 37 0040D446   call        printf (0040d6d0) 38 0040D44B   add         esp,8
39 13: 40 14:       return 0; 41 0040D44E   xor eax,eax 42 15: } 43 0040D450   pop edi 44 0040D451   pop esi 45 0040D452   pop ebx 46 0040D453   add esp,48h 47 0040D456   cmp ebp,esp 48 0040D458   call        __chkesp (0040d690) 49 0040D45D   mov esp,ebp 50 0040D45F   pop ebp 51 0040D460   ret

 

汇编代码:Release版

 1 ;int __cdecl main(int argc,const char **argv,const char **envp)
 2 _main proc near  3 
 4 arg_0= dword ptr 4
 5 
 6 mov eax,[esp+arg_0]  7 add  eax,3
 8 push eax  9 push offset Format    ; "nVarOne = %d \r\n"
10 call _printf 11 add  esp,8
12 xor eax,eax 13 retn
14 _main endp

 

虽然计算机只会做加法,但是可以通过补码转换将减法转变为加法形式来完成

设有二进制Y,其反码记为Y(反),假定其二进制长度为8位:

Y + Y(反) = 1111 1111B

Y + Y(反) + 1 = 0 (进位丢失)

Y(反) + 1 = 0 - Y <==> Y(反) + 1 = -Y <==>  Y(补) = -Y

 

例子:

源代码:

 1 #include <stdio.h>
 2 
 3 int main(int argc,char *argv[])  4 {  5     int nVarOne = argc;  6     int nVarTwo = 0;  7 
 8     scanf("%d",&nVarTwo);  9     
10     nVarOne = nVarOne - 100; 11     nVarOne = nVarOne + 5 - nVarTwo; 12 
13     printf("nVarOne = %d \r\n",nVarOne); 14 
15     return 0; 16 }

 

汇编代码:Debug版

 1 1: #include <stdio.h>  2 2:  3 3:    int main(int argc,char *argv[])  4 4: {  5 0040D400   push ebp  6 0040D401   mov ebp,esp  7 0040D403   sub esp,48h  8 0040D406   push ebx  9 0040D407   push esi 10 0040D408   push edi 11 0040D409   lea edi,[ebp-48h] 12 0040D40C   mov ecx,12h 13 0040D411   mov eax,0CCCCCCCCh 14 0040D416   rep stos dword ptr [edi] 15 5:        int nVarOne = argc; 16 0040D418   mov         eax,dword ptr [ebp+8] 17 0040D41B   mov         dword ptr [ebp-4],eax 18 6:        int nVarTwo = 0; 19 0040D41E   mov         dword ptr [ebp-8],0
20 7: 21 8:        scanf("%d",&nVarTwo); 22 0040D425   lea         ecx,[ebp-8] 23 0040D428   push ecx 24 0040D429   push        offset string "%d" (0042201c) 25 0040D42E   call        scanf (0040f940) 26 0040D433   add         esp,8
27 9: 28 10:       nVarOne = nVarOne - 100; 29 0040D436   mov         edx,dword ptr [ebp-4] 30 0040D439   sub edx,64h 31 0040D43C   mov         dword ptr [ebp-4],edx 32 11:       nVarOne = nVarOne + 5 - nVarTwo; 33 0040D43F   mov         eax,dword ptr [ebp-4] 34 0040D442   add         eax,5
35 0040D445   sub         eax,dword ptr [ebp-8] 36 0040D448   mov         dword ptr [ebp-4],eax 37 12: 38 13:       printf("nVarOne = %d \r\n",nVarOne); 39 0040D44B   mov         ecx,dword ptr [ebp-4] 40 0040D44E   push ecx 41 0040D44F   push        offset string "nVar = %d \r\n" (00422e80) 42 0040D454   call        printf (0040d6d0) 43 0040D459   add         esp,8
44 14: 45 15:       return 0; 46 0040D45C   xor eax,eax 47 16: } 48 0040D45E   pop edi 49 0040D45F   pop esi 50 0040D460   pop ebx 51 0040D461   add esp,48h 52 0040D464   cmp ebp,esp 53 0040D466   call        __chkesp (0040d690) 54 0040D46B   mov esp,ebp 55 0040D46D   pop ebp 56 0040D46E   ret

 

 

乘法运算对于汇编为imul 和 mul。由于乘法指令的执行周期较长,在编译过程中,编译器会先尝试将乘法转换成加法,或使用移位等周期较短的指令。

当它们都不可转换时,才回使用乘法指令:

例子:

源代码:

 1 #include <stdio.h>
 2 
 3 int main(int argc,char *argv[])  4 {  5     int nVarOne = argc;  6     int nVarTwo = argc;  7 
 8     printf("nVarOne * 15 = %d \r\n",nVarOne * 15);  9 
10     printf("nVarOne * 16 = %d \r\n",nVarOne * 16); 11 
12     printf("2 * 2 = %d",2 * 2); 13 
14     printf("nVarTwo * 4 + 5 = %d",nVarTwo * 4 + 5); 15 
16     printf("nVarOne * nVarTwo = %d",nVarOne * nVarTwo); 17 
18     return 0; 19 }

 

汇编代码:Debug版

 1 1: #include <stdio.h>  2 2:  3 3:    int main(int argc,char *argv[])  4 4: {  5 0040F9A0   push ebp  6 0040F9A1   mov ebp,esp  7 0040F9A3   sub esp,48h  8 0040F9A6   push ebx  9 0040F9A7   push esi 10 0040F9A8   push edi 11 0040F9A9   lea edi,[ebp-48h] 12 0040F9AC   mov ecx,12h 13 0040F9B1   mov eax,0CCCCCCCCh 14 0040F9B6   rep stos dword ptr [edi] 15 5:        int nVarOne = argc; 16 0040F9B8   mov         eax,dword ptr [ebp+8] 17 0040F9BB   mov         dword ptr [ebp-4],eax 18 6:        int nVarTwo = argc; 19 0040F9BE   mov         ecx,dword ptr [ebp+8] 20 0040F9C1   mov         dword ptr [ebp-8],ecx 21 7: 22 8:        printf("nVarOne * 15 = %d \r\n",nVarOne * 15); 23 0040F9C4   mov         edx,dword ptr [ebp-4] 24 0040F9C7   imul edx,edx,0Fh 25 0040F9CA   push edx 26 0040F9CB   push        offset string "nVarOne * 15 = %d \r\n" (0042304c) 27 0040F9D0   call        printf (0040d6d0) 28 0040F9D5   add         esp,8
29 9: 30 10:       printf("nVarOne * 16 = %d \r\n",nVarOne * 16); 31 0040F9D8   mov         eax,dword ptr [ebp-4] 32 0040F9DB   shl         eax,4
33 0040F9DE   push eax 34 0040F9DF   push        offset string "nVarOne * 16 = %d \r\n" (00423034) 35 0040F9E4   call        printf (0040d6d0) 36 0040F9E9   add         esp,8
37 11: 38 12:       printf("2 * 2 = %d",2 * 2); 39 0040F9EC   push        4
40 0040F9EE   push        offset string "nVarOne = %d \r\n" (00422e80) 41 0040F9F3   call        printf (0040d6d0) 42 0040F9F8   add         esp,8
43 13: 44 14:       printf("nVarTwo * 4 + 5 = %d",nVarTwo * 4 + 5); 45 0040F9FB   mov         ecx,dword ptr [ebp-8] 46 0040F9FE   lea         edx,[ecx*4+5] ;lea指令在汇编中的妙用,用于计算数值,简单 47 0040FA05   push edx 48 0040FA06   push        offset string "nVarTwo * 4 + 5 = %d" (0042301c) 49 0040FA0B   call        printf (0040d6d0) 50 0040FA10   add         esp,8
51 15: 52 16:       printf("nVarOne * nVarTwo = %d",nVarOne * nVarTwo); 53 0040FA13   mov         eax,dword ptr [ebp-4] 54 0040FA16   imul        eax,dword ptr [ebp-8] 55 0040FA1A   push eax 56 0040FA1B   push        offset string "nVarOne * nVarTwo = %d" (00423004) 57 0040FA20   call        printf (0040d6d0) 58 0040FA25   add         esp,8
59 17: 60 18:       return 0; 61 0040FA28   xor eax,eax 62 19: } 63 0040FA2A   pop edi 64 0040FA2B   pop esi 65 0040FA2C   pop ebx 66 0040FA2D   add esp,48h 67 0040FA30   cmp ebp,esp 68 0040FA32   call        __chkesp (0040d690) 69 0040FA37   mov esp,ebp 70 0040FA39   pop ebp 71 0040FA3A   ret

 

1 printf("nVarTwo * 9 + 5 = %d",nVarTwo + 5); 2 
3 0040B8F3   mov        eax,dword ptr [ebp-8] 4 0040B8F6   imul        eax,eax,9
5 0040B8F9   add        eax,5
6 0040B8FC   push eax 7 0040B8FD   push        offset string "nVarTwo * 9 + 5 = %d" (0041ff7c) 8 0040B902   call        printf(0040b750) 9 0040B907   add        esp,8

 

汇编代码:Release版

 1 arg_0 = dword ptr 4
 2 
 3 
 4 push esi  5 mov        esi,[esp+4+arg_0]  6 
 7 lea        eax,[esi+esi*2]  8 lea        eax,[eax+eax*4]  9 push eax 10 push    offset aNvarone15D ; "nVarOne * 15 = %d"
11 call _printf 12 
13 mov ecx,esi 14 shl        ecx,4
15 push ecx 16 push    offset aNvarone16D ; "nVarOne * 16 = %d"
17 call _printf 18 
19 push    4
20 push    offset a22D ; "2 * 2 = %d"
21 call _printf 22 
23 lea        edx,ds:5[esi*4] 24 push edx 25 push    offset aNvartwo45D ; "nVarTwo * 4 + 5 = %d"
26 call _printf 27 
28 lea        eax,[esi+esi*8+5] 29 push eax 30 push    offset aNvartwo95D ; "nVarTwo * 9 + 5 = %d"
31 call _printf 32 
33 mov ecx,esi 34 imul ecx,esi 35 push ecx 36 push    offset aNvaroneNvarTwo  ; "nVarOne * nVarTwo = %d"
37 call _printf 38 
39 add esp,30h 40 pop        esi

 

C++中的除法和数学中的除法不同。在C++中,除法运算不保留余数,有专门求取余数的运算(运算符%),就是取模运算。

对于整数除法,C++的规则是仅仅保留整数部分,小数部分完全舍弃

在C语言中的除法规则:

>两个无符号整数相除,结果依然为无符号

>两个有符号整数相除,结果则是有符号的

>有符号和无符号混除,结果则是无符号的,有符号的最高位(符号位)被作为数据对待,然后作为无符号数参与计算

 

对于处理小数部分计算机通常有几种方式:

>向下取整

就是取得 往 负无穷 方向的最接近x的整数值,也就是取得不大于x的最大值

数学中用[x]表示向下取整 , C语言中用floor()函数(math.h),也被称为“地板取整”

但是向下取整存在一个问题:

      -a                   a

 [  —— ]  !=  -  [ —— ]              在a/b不为整数的情况下

      b                    b

即 一正相除一负的取整不等于两正相除取整的负

 

>向上取整

同理,向上取整就是  正无穷 方向上最接近x的整数

C语言中 ceil()函数,也被称为“天花板取整”

向下取整同样存在上面的问题:

      -a                    a

  [  —— ]  !=  -  [ —— ]              在a/b不为整数的情况下

       b                    b

 

即 一正相除一负的取整不等于两正相除取整的负

 

>向零取整

就是取得  向0方向上 最接近于x的整数值。也就是放弃小数

向零取整除法满足:

     -a             a                   a

  [ —— ] = [ —— ]  =  - [ —— ]

      b            -b                   b

 

 

 

 

在C语言和其他很多语言中对整数除法规定为向零取整。也被称为“截断除法”

 

 1 #include <stdio.h>
 2 
 3 int main()  4 {  5     printf("8 %% -3 = %d\r\n", 8 % -3); //2  6     printf("-8 %% -3 = %d\r\n", -8 % -3); //-2  7     printf("-8 %% 3 = %d\r\n",-8 % 3); //-2  8 
 9     return 0; 10 }


设被除数为a,除数为b,商为q,余数为r,则有以下性质:

> |r| < |b|

> a = b*q + r

>b = (a - r) / q

>q = (a - r) / b

>r = a - q*b

 

我们计算就要按着这个公式,当然可以巧记(取模运算符号和被除数的一致)

看下面例子:

源代码:

 

 1 #include <stdio.h>
 2 
 3 int main(int argc,char *argv[])  4 {  5     int nVarOne = argc;  6     int nVarTwo = argc;  7 
 8     printf("nVarOne / nVarTwo = %d",nVarOne/nVarTwo);  9     printf("nVarOne / 2 = %d",nVarOne / 2); 10     printf("nVarTwo / 7 = %d",nVarTwo / 7); 11     printf("nVarTwo % 7 = %d",nVarTwo % 7); 12     printf("nVarOne / 8 = %d",nVarOne / 8); 13 
14     return 0; 15 }

 

 

汇编代码:

 

 1 1: #include <stdio.h>  2 2:  3 3:    int main(int argc,char *argv[])  4 4: {  5 0040D710   push ebp  6 0040D711   mov ebp,esp  7 0040D713   sub esp,48h  8 0040D716   push ebx  9 0040D717   push esi 10 0040D718   push edi 11 0040D719   lea edi,[ebp-48h] 12 0040D71C   mov ecx,12h 13 0040D721   mov eax,0CCCCCCCCh 14 0040D726   rep stos dword ptr [edi] 15 5:        int nVarOne = argc; 16 0040D728   mov         eax,dword ptr [ebp+8] 17 0040D72B   mov         dword ptr [ebp-4],eax 18 6:        int nVarTwo = argc; 19 0040D72E   mov         ecx,dword ptr [ebp+8] 20 0040D731   mov         dword ptr [ebp-8],ecx 21 7: 22 8:        printf("nVarOne / nVarTwo = %d",nVarOne/nVarTwo); 23 0040D734   mov         eax,dword ptr [ebp-4] 24 0040D737   cdq ;扩展高位
25 0040D738   idiv        eax,dword ptr [ebp-8] 26 0040D73B   push eax 27 0040D73C   push        offset string "nVarOne / nVarTwo = %d" (00422ff0) 28 0040D741   call        printf (00401080) 29 0040D746   add         esp,8
30 9:        printf("nVarOne / 2 = %d",nVarOne / 2); 31 0040D749   mov         eax,dword ptr [ebp-4] 32 0040D74C   cdq
33 0040D74D   sub eax,edx 34 0040D74F   sar         eax,1
35 0040D751   push eax 36 0040D752   push        offset string "nVarOne / 2 = %d" (00422fdc) 37 0040D757   call        printf (00401080) 38 0040D75C   add         esp,8
39 10:       printf("nVarTwo / 7 = %d",nVarTwo / 7); 40 0040D75F   mov         eax,dword ptr [ebp-8] 41 0040D762   cdq
42 0040D763   mov         ecx,7
43 0040D768   idiv eax,ecx 44 0040D76A   push eax 45 0040D76B   push        offset string "nVarTwo / 7 = %d" (00422044) 46 0040D770   call        printf (00401080) 47 0040D775   add         esp,8
48 11:       printf("nVarTwo % 7 = %d",nVarTwo % 7); 49 0040D778   mov         eax,dword ptr [ebp-8] 50 0040D77B   cdq
51 0040D77C   mov         ecx,7
52 0040D781   idiv eax,ecx 53 0040D783   push edx 54 0040D784   push        offset string "nVarTwo % 7 = %d" (00422030) 55 0040D789   call        printf (00401080) 56 0040D78E   add         esp,8
57 12:       printf("nVarOne / 8 = %d",nVarOne / 8); 58 0040D791   mov         eax,dword ptr [ebp-4] 59 0040D794   cdq
60 0040D795   and         edx,7
61 0040D798   add eax,edx 62 0040D79A   sar         eax,3
63 0040D79D   push eax 64 0040D79E   push        offset string "nVarOne / 8 = %d" (0042201c) 65 0040D7A3   call        printf (00401080) 66 0040D7A8   add         esp,8
67 13: 68 14:       return 0; 69 0040D7AB   xor eax,eax 70 15: } 71 0040D7AD   pop edi 72 0040D7AE   pop esi 73 0040D7AF   pop ebx 74 0040D7B0   add esp,48h 75 0040D7B3   cmp ebp,esp 76 0040D7B5   call        __chkesp (00401100) 77 0040D7BA   mov esp,ebp 78 0040D7BC   pop ebp 79 0040D7BD   ret

 

 

 1 M与-M在计算机中的表示是互为补码的  2 即 [-M]=[M]补  3 因此 ,[M]/2分2个情况考虑  4 1,M为正数,正数的除法就是算术右移一位  5    mov eax , M  6    sar eax,1 //右移1位,即除以2  7 2,M为负数,则[M]/2= [ [-M]/2 ]补   = [-[[M]补/2] ]补  8 M为负数,所以,上面的计算过程是:  9 M取反加1,算术右移1位,再取反加1 10 设M为1字节 11 M取反加1可以表示成   (FF-M+1) 12 因此,上面的计算过程转化为 13 FF - ( (FF-M+1)/2 ) +1 = FF-(FF/2) + (M+1)/2  
14 这里的 /2意思为向右带符号移一位,而FF 算术右移1位还是FF 15 所以可以简化为 16 (M+1)/2   
17 注意,这里的M是负数 18 所以: 19 mov eax, M 20 sub eax,-1   //减-1就是+1
21 sar eax,1 //右移1位,除以2 22 然后解释一下 CDQ指令就可以了 23 当EAX >=0 ,CDQ结果   EDX=0
24 当EAX < 0 ,CDQ结果   EDX=-1
25 因此,M/2可以写成 26 mov eax, M 27 cdq //扩展符号位,到EDX 28 sub eax,edx    //EAX>0 ,则EAX - 0 ;EAX<0 ,则EAX - (-1) 
29 sar eax,1      //右移2位

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM