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位