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位