C/C++ 反匯編-針對加減乘除的還原


算術運算通常是指,加減乘除四則運算,而計算機中的四則運算與數學中的有所不同,同樣是實現算術運算,高級語言與匯編語言的實現思路完全不同,往往一個簡單的減法運算,都要幾條指令的配合才能得出計算結果,而為了保證程序的高效率,編譯器會對其進行最大限度地優化,這就涉及到匯編代碼的逆推,如下筆記則是整理的逆推常用手法。

一般VS系列編譯器對代碼的優化有兩種方案,O1方案則可生成占用空間最小的文件,O2方案則注重執行效率最快,編譯器在Release模式下會采用O2方式對代碼效率進行優化,所以我們有必要好好研究一下其到底將代碼優化成了啥樣子,這里為了方便演示我會使用匯編語言模擬編譯器生成代碼的思路。

加法優化/減法優化

加法常量優化: 當計算結果中出現了兩個常數相加的情況,且中間該變量沒有被改變過,則就會被優化掉。

#include <stdio.h>
#include <windows.h>

int main(int argc,char * argv[])
{
	int x = 10;
	int y = 20;

	printf("%d \n", x + y);

	return 0;
}

如下,我們只看到了一個push 0x1E 編譯器發現我們的x + y是兩個常數,則為了效率,直接將結果計算出來打印了。

如果我們將代碼這樣寫,那么加法運算將不會被優化掉,因為編譯器無法確定,表達式的結果,只能運行后動態計算。

#include <stdio.h>
#include <windows.h>

int main(int argc,char * argv[])
{
	int x = 10;
	int y = 0;

	for (int y = 0; y < 10; y++)
	{
		printf("%d \n", x + y);
	}
	return 0;
}

如下是反匯編后得到的結果,通常情況下加法指令是ADD運算才對,下方代碼中並沒有出現Add指令,我們的加法計算其實是轉化為了lea eax, ds:[esi+0xA],這條指令不僅可以取地址,還可以用來計算加減等運算,lea指令允許用戶在一個時鍾周期內完成加減法的計算過程,其效率遠比add,sub指令高。

這里我直接使用匯編語言來模擬實現編譯器對加減法的實現流程,如下代碼所示。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		; 針對加法的lea指令優化
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],3
		mov eax,dword ptr ds:[x]
		mov ebx,dword ptr ds:[y]
		
		mov eax,dword ptr ds:[x]
		lea eax,dword ptr ds:[eax + 3]         ; eax = eax + 3
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[x]
		lea eax,dword ptr ds:[eax + ebx + 2]   ; eax = eax + ebx + 2
		invoke crt_printf,addr szFmt,eax
		
		; 針對減法的lea指令優化
		mov dword ptr ds:[x],6
		mov eax,dword ptr ds:[x]
		
		lea eax,dword ptr ds:[eax - 2]         ; eax = eax - 2
		invoke crt_printf,addr szFmt,eax
		
		; 加減法混合優化
		mov eax,10
		mov ebx,15
		lea ebx,dword ptr ds:[eax + ebx - 3 ]  ; ebx = eax + ebx - 3
		invoke crt_printf,addr szFmt,ebx

		invoke ExitProcess,0
	main ENDP
END main

接着我們來完成一個三數相加的案例,比如說將x+y+100的結果輸出,匯編代碼如下。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],-5
		mov dword ptr ds:[y],3
		
		mov eax,dword ptr ds:[x]
		mov ebx,dword ptr ds:[y]
		
		lea ecx,dword ptr ds:[eax + ebx + 100]
		invoke crt_printf,addr szFmt,ecx

		invoke ExitProcess,0
	main ENDP
END main

加減法中的常量傳播: 將編譯期間可計算出結果的變量轉換成常量,這樣就減少了變量的使用,如下,由於printf輸出的是一個常量,則編譯器會對其進行處理,在編譯期間計算出變量結果后,直接使用常量10來代替,於是printf("value => %d \n", x);等價於printf("value => %d \n", 10);.

int main(int argc,char * argv[])
{
	int x = 10;
	int y = 0;

	printf("value => %d \n", x);
	return 0;
}

加減法中的常量折疊: 當計算公式中存在多個常量進行計算時,且編譯器可以在編譯時動態計算出結果,這樣源碼中所有的常量計算過程將會被替換掉。

int main(int argc,char * argv[])
{
	int x = 10;
	int y = 0;

	int value = 1 + 2 * 3 + 7;

	printf("value => %d \n", value);
	return 0;
}

如上代碼中,我們的計算表達式在整個程序運行期間沒有發生過變化,則VS編譯器在開啟O2優化后,會首先計算出int value = 1 + 2 * 3 + 7;表達式的值並將其替換成一個常量值,在打印函數中直接打印計算后的結果,編譯器會刪除計算的變量,直接替換為常量。

常量折疊+常量傳播: 如下代碼中由於nVarOne = nVarOne + 1;計算結果是一個常量,則在編譯會會被直接替換掉,第二句則滿足常量折疊,計算后保留常量值3,最后的加法nVarOne + nVarTwo;雖然在加兩個變量,但變量數值未發生變化,同樣會被優化為常量.

int main(int argc,char * argv[])
{
	int nVarOne = 0;
	int nVarTwo = 0;

	// 變量加常量加法運算
	nVarOne = nVarOne + 1;       // 0+1 變為 nVarOne = 1
	nVarOne = 1 + 2;             // 常量折疊

	// 兩個常量相加的加法運算
	nVarOne = nVarOne + nVarTwo;
	printf("value = > %d \n", nVarOne);
	return 0;
}

如果我們將初始化參數通過命令行獲取的話,由於argc在編譯期間無法被確定,所以編譯器無法在編譯時計算出結果,那么程序中的變量將不會被常量替換掉,依然執行加法或減法運算。

int main(int argc,char * argv[])
{
	int nVarOne = argc;
	int nVarTwo = argc;

	nVarOne = nVarOne + 1;
	nVarOne = nVarOne + nVarTwo;
	printf("value = > %d \n", nVarOne);
	return 0;
}

減法計算轉加法: 減法計算通常使用sub來時間,但計算機只會做加法,如果想要計算減法,只需要通過補碼轉換將減法轉換為加法來計算即可,例如加一個負數同樣也相當於減去一個正數。

例如: 5-2 可轉換成 5 + (0-2) => 5 + (2(取反)+1) => 5 + 2 補

int main(int argc,char * argv[])
{
	int nVarOne = 0;
	int nVarTwo = 0;

	// 防止被優化
	scanf("%d", &nVarOne);
	scanf("%d", nVarTwo);
	
	nVarOne = nVarOne - 10;
	nVarOne = nVarOne + 5 - nVarTwo;
	printf("varOne = %d \n", nVarOne);
	return 0;
}

在某些編譯器中,減法運算會通過加法來實現,其實現匯編代碼是這個樣子的,在實際逆向過程中,加法與減法可以相互轉換,只要得到的結果是正確的均可。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		; 針對加法的lea指令優化
		mov dword ptr ds:[x],100
		mov dword ptr ds:[y],3
		mov eax,dword ptr ds:[x]
		
		; 例如 100 - 10 可轉換為 => 100 + (-10)
		mov eax,dword ptr ds:[x]
		add eax,0FFFFFFF0h
		invoke crt_printf,addr szFmt,eax

		invoke ExitProcess,0
	main ENDP
END main

乘法優化

在匯編語言中,乘法指令通常是使用mul/imul來計算,其分別針對的是無符號與有符號乘法,由於乘法指令在執行時所消耗的時鍾周期較長,所以在編譯時,編譯器會先嘗試將其轉換為加法,或者使用shr/shl等移位指令來替換,當兩者都無法進行優化時,才會使用原始的乘法指令計算。

使用LEA指令替換乘法: 使用lea計算乘法運算時,必須要保證乘數是2的次冪,而且范圍必須是2/4/8

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		; 針對乘法的lea指令優化
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],3
		
		mov eax,dword ptr ds:[x]
		xor ebx,ebx
		lea ebx,dword ptr ds:[eax * 8 + 2]     ; ebx = eax * 8 + 2
		invoke crt_printf,addr szFmt,ebx
		
		invoke ExitProcess,0
	main ENDP
END main

如果我們計算的乘法超出了該范圍,則需要對乘法進行拆分,拆分時也應遵循2的次冪,拆分后在分開來計算,如下我們需要計算 15 * eax的結果,拆分過程如下。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		; 針對乘法的lea指令優化
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],3
		
		; 如果使用lea計算乘法,則乘數必須是2/4/8
		mov eax,dword ptr ds:[y]               ; eax = 3 => 計算 15 * eax
		lea edx,dword ptr ds:[eax * 4 + eax]   ; edx = 4eax + eax => 5eax
		lea edx,dword ptr ds:[edx * 2 + edx]   ; edx = 5eax * 2 + 5eax => 15eax
		invoke crt_printf,addr szFmt,edx       ; edx = eax * 15 = 45
		
		invoke ExitProcess,0
	main ENDP
END main

如果計算乘法時乘數非2的次冪,這種情況下需要減去特定的值,例如當我們計算eax * 7時,由於7非二的次冪,我們無法通過lea指令進行計算,但我們可以計算eax * 8計算出的結果減去一個eax同樣可以得到正確的值,例如計算eax * 7 + 10的結果。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		; 針對乘法的lea指令優化
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],3
		
		; 如果計算乘法時乘數非2的次冪,則此時需要減
		mov eax,dword ptr ds:[y]               ; eax = 3 => 計算 eax * 7 + 10
		lea edx,dword ptr ds:[eax * 8]         ; edx = eax * 8
		sub edx,eax                            ; edx = edx - eax
		add edx,10                             ; edx = edx + 10
		invoke crt_printf,addr szFmt,edx       ; edx = eax * 7 + 10
		
		mov eax,dword ptr ds:[y]               ; eax = 3 => 計算 eax * 3 - 7
		lea edx,dword ptr ds:[eax * 2]         ; edx = eax * 2
		add edx,eax                            ; edx = edx + eax
		sub edx,7                              ; edx = edx - 7
		invoke crt_printf,addr szFmt,edx       ; edx = eax * 3 - 7
		
		invoke ExitProcess,0
	main ENDP
END main

通過使用邏輯與算數,左移,同樣可以實現2的次冪的高速乘法運算,如果滿足特定條件,編譯器生成的代碼就會呈現出以下案例中所描述的代碼特點。

.data
	x DWORD ?
	y DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],-5
		mov dword ptr ds:[y],3
		
		; 邏輯左移(無符號乘法)
	; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
	; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
	
		mov eax,dword ptr ds:[y]
		shl eax,1        ; eax = eax * 2 ^ 1     eax * 2
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[y]
		shl eax,2        ; eax = eax * 2 ^ 2     eax * 4
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[y]
		shl eax,3        ; eax = eax * 2 ^ 3     eax * 8
		invoke crt_printf,addr szFmt,eax
		
		; 算數左移(有符號乘法)
		mov eax,dword ptr ds:[x]
		sal eax,1         ; eax = eax * 2^1      eax * 2
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[x]
		sal eax,2         ; eax = eax * 2^2      eax * 4
		invoke crt_printf,addr szFmt,eax
		
		invoke ExitProcess,0
	main ENDP
END main

乘法優化的基本就這些知識點,除了兩個未知變量的相乘無法優化外,其他形式的乘法運算均可以進行優化,如果表達式中存在一個常量值,那編譯器則會匹配各種優化策略,最后對不符合優化策略的運算進行調整,如果真的無法優化,則會使用原始乘法指令計算。

除法優化

通常情況下計算除法會使用div/idiv這兩條指令,該指令分別用於計算無符號和有符號除法運算,但除法運算所需要耗費的時間非常多,大概需要比乘法運算多消耗10被的CPU時鍾,在Debug模式下,除法運算不會被優化,但Release模式下,除法運算指令會被特定的算法經過優化后轉化為為乘法,這樣就可以提高除法運算的效率。

關於除法運算總結

  1. 如果被除數是一個未知數,那么編譯器無法確定數值,則編譯器會使用原始的div命令計算,程序的執行效率會變低。
  2. 如果除數是二的次冪,那么可以將其轉化為處理速度快的 shr a,n 指令,該指令的執行只需要1個時鍾周期,效率最高。
  3. 若進行二的次冪,有符號運算,則只需要使用 sha 進行快速除法運算。

除數為正2的次冪優化(無符號): 如果除數為2的次冪,那么就會使用移位運算替代除法運算,2的次冪還原非常容易,只需要找到移位次數即可得出除以的是多少。

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],5

		; ----------------------------------------------------
		; 【除數為2的優化方式】
		; 被除數為正數(無需擴展): eax => 5 / 2 = 2
		mov eax,dword ptr ds:[x]   ; 被除數
		sar eax,1                  ; 算數右移
		invoke crt_printf,addr szFmt,eax
		
		
		; ----------------------------------------------------
		; 【除數為4的優化方式】
		; 被除數為正數(無需擴展): eax => 5 / 4 = 1
		mov eax,dword ptr ds:[x]
		sar eax,2
		invoke crt_printf,addr szFmt,eax
		
		; ----------------------------------------------------
		; 【除數為8的優化方式】
		; 被除數為正數(無需擴展): eax => 5 / 8 = 0
		mov eax,dword ptr ds:[x]
		sar eax,3
		invoke crt_printf,addr szFmt,eax
		
		invoke ExitProcess,0
	main ENDP
END main

除數為負2的次冪優化(有符號): 當除數為負數時,且為2的次冪的情況下,編譯器生成代碼時這樣的,其還原方式為取得shr eax,xx中的次數,與被除數相除,最后neg取反即可。

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],10
		mov dword ptr ds:[z],-10

		; 除數為(有符號)負2的次冪的計算過程
		mov eax,dword ptr ds:[y]    ; y = 10
		cdq                         ; 符號擴展edx : eax
		sub eax,edx                 ; 減去符號位
		sar eax,1                   ; eax = 10 / -2
		neg eax                     ; 將正數 eax 翻轉為負數 = -5
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[y]    ; y = 10
		cdq
		and edx,3
		add eax,edx
		sar eax,2                   ; eax = 10 / -4
		neg eax                     ; eax = -2
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[z]    ; z = -10 
		cdq
		and edx,7
		add eax,edx
		sar eax,3                   ; eax = -10 / -8 
		neg eax                     ; eax = 1 (負負得正)
		invoke crt_printf,addr szFmt,eax
		
		invoke ExitProcess,0
	main ENDP
END main

除數為負數的優化(無符號): 如果被除數是一個負數,除數依然是2的次冪,則此時計算后只需要去掉neg取反即可得到正確結果,逆推方式同除數為負2的次冪優化保持一致。

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],-5
		mov dword ptr ds:[y],10
		mov dword ptr ds:[z],-10

		; 被除數為(有符號)的計算過程
		mov eax,dword ptr ds:[z]
		cdq
		sub eax,edx
		sar eax,1                  ; eax = -10 / 2
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[x]
		cdq
		and edx,3
		add eax,edx
		sar eax,2                  ; eax = -5 / 4
		invoke crt_printf,addr szFmt,eax
		
		mov eax,dword ptr ds:[z]
		cdq
		and edx,7
		add eax,edx
		sar eax,3                   ; eax = -10 / 8
		invoke crt_printf,addr szFmt,eax
		
		; 如果同時為負數的情況
		mov eax,dword ptr ds:[z]    ; z = -10 
		cdq
		and edx,7
		add eax,edx
		sar eax,3                   ; eax = -10 / -8 
		neg eax                     ; eax = 1 (負負得正)
		invoke crt_printf,addr szFmt,eax
		
		invoke ExitProcess,0
	main ENDP
END main

除數為正非2的次冪優化(有符號): 上方的除法運算被除數均為2的次冪,除數的范圍也被限定在了2/4/8這樣的范圍之內,如下是計算非2的次冪的計算方式,如果需要知道除數是多少則可以使用公式2^(32+n) / M計算后得出.

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],5
		mov dword ptr ds:[y],10
		mov dword ptr ds:[z],-10
		
		; 除法(有符號)非2的冪轉換為乘法
		
		mov ecx,dword ptr ds:[y]      ; 被除數 ecx = 10 / 3 = 3
		mov eax,055555556h            ; eax = M值 1431655766
		imul ecx
		mov eax,edx                   ; edx = n 計算: 2^(32+n) / M
		shr eax,01fh                  ; 計算出除數為 2.9999 => 3
		add edx,eax
		invoke crt_printf,addr szFmt,edx
		
		
		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 5 = 2
		mov eax,066666667h             ; 此處的M模值是編譯器計算后得到的
		imul ecx
		sar edx,1                      ; 想要知道除數是多少,只需要
		mov eax,edx                    ; 2^(32 + edx) / M = 2^33 / 66666667 = 5
		shr eax,01fh
		add edx,eax
		invoke crt_printf,addr szFmt,edx
		
		
		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 6 = 1
		mov eax,02AAAAAABh             ; eax = 715827883
		imul ecx
		mov eax,edx                    ; 2^(32 + edx) / M = 2^32 / 2AAAAAAB = 6
		shr eax,01fh
		add edx,eax
		invoke crt_printf,addr szFmt,edx
		
		mov ecx,dword ptr ds:[z]       ; ecx = -10 / 9 = -1
		mov eax,038E38E39h             ; eax = 954437177 
		imul ecx
		sar edx,1                      ; 2^(32 + edx) / M = 2^33 / 38E38E39 = 9
		mov ecx,edx
		shr ecx,01fh
		add edx,ecx
		invoke crt_printf,addr szFmt,edx
		
		invoke ExitProcess,0
	main ENDP
END main

先來看第一段匯編代碼,我們此時已知M = 055555556h 且 edx = N帶入公式2^(32+n) / M,由於edx沒有變化所以此處應計算2^32 / 055555556h = 2.9999 即可計算出此處是除以的3

mov ecx,dword ptr ds:[y]      ; 被除數
mov eax,055555556h            ; M值 => 此處的M模值是編譯器計算后得到的
imul ecx
mov eax,edx                   ; edx = N
shr eax,01fh
add edx,eax
invoke crt_printf,addr szFmt,edx

再來看另一段,如下所示,這段代碼中sar edx,1edx的值發生過一次變化,所以公式中應該加上變化的一次計算得到 2^33 / 66666667 = 5 除數是5

mov ecx,dword ptr ds:[y]       ; ecx = 10 / 5 = 2
mov eax,066666667h             ; 此處的M模值是編譯器計算后得到的
imul ecx
sar edx,1                      ; 想要知道除數是多少,只需要
mov eax,edx                    ; 2^(32 + edx) / M = 2^33 / 66666667 = 5
shr eax,01fh
add edx,eax
invoke crt_printf,addr szFmt,edx

除數為正數非2的次冪優化(無符號): 上方代碼中的除法計算是針對有符號數進行的,如果是針對無符號數則需要以下方式計算.

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],-5
		mov dword ptr ds:[y],10
		mov dword ptr ds:[z],20
		
		; 除法(無符號)非2的次冪(正數)轉換為乘法
		
		xor edx,edx
		mov ecx,dword ptr ds:[y]    ; ecx = 10
		mov eax,0AAAAAAABh          ; ecx / 3 = 3
		mul ecx
		shr edx,1
		invoke crt_printf,addr szFmt,edx
		
		; 還原除數: 2 ^(32 + n) / M => 2 ^ (32+2) / 0CCCCCCCDh = 5
		xor edx,edx
		mov ecx,dword ptr ds:[y]    ; ecx = 10 => 計算: 10/5
		mov eax,0CCCCCCCDh          ; eax = M
		mul ecx
		shr edx,2                   ; edx= n
		invoke crt_printf,addr szFmt,edx
		
		; 還原除數: 2 ^(32 + n) / M => 2 ^ (32+2) / 0AAAAAAABh = 6
		xor edx,edx
		mov ecx,dword ptr ds:[y]     ; ecx = 10 => 計算:10/6
		mov eax,0AAAAAAABh           ; eax = M
		mul ecx
		shr edx,2                    ; edx = n
		invoke crt_printf,addr szFmt,edx
		
		
		;還原除數: 2 ^(32 + n) / M => 2 ^ 33 / 038E38E39h = 9
		xor edx,edx
		mov ecx,dword ptr ds:[z]     ; ecx = 20  => 計算: 20/9
		mov eax,038E38E39h           ; eax = M
		mul ecx
		shr edx,1                    ; edx = n
		invoke crt_printf,addr szFmt,edx
		invoke ExitProcess,0
	main ENDP
END main

除數為負數非2的次冪優化(無符號):

.data
	x DWORD ?
	y DWORD ?
	z DWORD ?
	szFmt BYTE '計算結果: %d',0dh,0ah,0
.code
	main PROC
		mov dword ptr ds:[x],-5
		mov dword ptr ds:[y],10
		mov dword ptr ds:[z],20
		; 還原除數: 2 ^(32 + n) / M => 2 ^ 33 / 0AAAAAAABh = nge(3) => -3
		xor edx,edx
		mov ecx,dword ptr ds:[z]      ; ecx = 20  => 計算: 20/-3
		mov eax,0AAAAAAABh            ; eax = M
		mul ecx
		shr edx,1                     ; edx = n 
		neg edx                       ; edx=6 結果neg取反
		invoke crt_printf,addr szFmt,edx
		
		; 還原除數: 2 ^(32 + n) / M => 2 ^ 62 / 040000001h = 4294967292
		xor edx,edx
		mov ecx,dword ptr ds:[y]       ; ecx = 10 => 計算: 10 / -3
		mov eax,040000001h             ; eax = M
		mul ecx
		shr edx,01eh                   ; edx = n
		invoke crt_printf,addr szFmt,edx
		
		invoke ExitProcess,0
	main ENDP
END main

看一下64位除法,編譯后載入觀察,一摸一樣,不用再寫了。

#include <stdio.h>
#include <windows.h>

int main(int argc,char * argv[])
{
	int x, y, z;
	scanf("%d", &x);

	z = x / 3;
	printf("%d \n", z);

	z = x / 5;
	printf("%d \n", z);

	return 0;
}

看看 z = x / 5 + 3 - 2; 優化后變成了 z = x / 5 + 1 夠毒。

繼續改改。

int main(int argc,char * argv[])
{
	int x, y, z;
	scanf("%d", &x);

	z = x / 3;
	printf("%d \n", z);

	z = x / 5 + 3 - 2;
	y = z * x + 4;

	printf("%d \n", y);

	return 0;
}

再來看看64位版無符號數。

int main(int argc,char * argv[])
{
	unsigned int x, y, z;
	scanf("%d", &x);

	y = x / -3;

	printf("%d \n", y);

	z = x / -5;
	printf("%d \n", z);
	return 0;
}

還原除數:2^32+1E / 0x40000001 = 2^62 / 1073741825 = 4294967292.0000000037252902949925 = FFFFFFFC

還原除數:2^32+0x1F / 0x80000003 = 2^63 / 2147483651 = 4294967290.0000000083819031598299 = FFFFFFFA


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM