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位