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位

 

 

 

 

posted @ 2012-10-31 09:43  lfsblack  阅读(5797)  评论(0编辑  收藏  举报