這次依然是從彙編開始分析C語言裡的一些語法實現,這次還是Windows平臺上為主~

直接上最簡單的:

 1int z = 0;
 2char s1 = (char)z;
 3
 4// ASM
 5int* z = 0;
 6// 這次就直接顯示符號名吧,不看內存地址了
 7// dword ptr就是數據類型聲明
 8mov         dword ptr [z],0
 9char* s1 = (char*)z;
10// movzx這個指令,下面講解,總之就是把z移動到eax寄存器.
11movzx       eax,byte ptr [z]
12// 直接粗暴的進行一個強轉,al是AX的第八位,AX是EAX的低16位
13// mov指令要求操作數大小相等,byte ptr只佔8個字節,所以這裡用AL寄存器
14mov         byte ptr [s1],al

movzx這個指令簡單來說就是把小字節操作數移動到大字節寄存器,同時讓大字節的高位補零。

為什麼這麼設計,我們從上面這個movzx這行代碼說起,[z]是byte ptr類型,只佔用8位也就是1字節。

eax是32位寄存器,能存放4字節,[z]放進eax只能佔用低八位,高24位完全用不到,這裡就需要用到高位補零了,就是因為怕高位還有字節沒清零的情況,導致後續計算直接崩了。


接著看指針運算,這個其實早就學過了,這次只是溫故一下,理解指針運算很重要的一點就是在C語言中,數據類型其實毫無意義,所有東西都可以當作內存來看,只是一個內存分配大小的問題。

 1
 2	// Q: 為什麼1能轉換成指針?
 3	// A: 別惦記這你那逼數據類型了
 4	char* cp = (char*)1;
 5	// char* cp = 1
 6
 7	// cp被轉化成int*了,指針指向位int類型,4字節,運算符在指針上的運算為位移,+2就意味著移動兩格總計8字節
 8	// 所以i1的值是9
 9	// 簡單來說類型轉換,不會改變值,但是會改變運算方式
10	int i1 = (int)((int*)cp + 2);
11
12	// 這裡也一樣,i2為17
13	int* i2 = (int*)i1 + 2;
14
15	// short為2字節,但這裡並非指針,只是普通的算術運算
16	// s = 95
17	short s = ((short)i2 + 2) * 5;
18
19	// 這裡比較特別,i2被轉化為2級指針,int**指向的類型是int*,指針類型佔用的字節是8字節
20	// 所以是17 + 8 = 25
21	int** i3 = (int**)i2 + 1;

函數指針,這個東西主要就看看在彙編中是怎麼實現的。

 1
 2add(1, 2);
 3call        add (07FF652F01311h)
 4
 5void* a = add;
 6// 實際也沒什麼特別的,就是把add的函數地址給了進去
 7lea         rax,[add (07FF6F9311311h)]
 8mov         qword ptr [a],rax
 9
10//我們也能做點其他的,比如把這個地址的數據讀出來
11int* a = (int*)add;
12int as = *a; // 加*就是解引用,讀裡面的值, 不加*就只是讀取內存地址

image.png

運行完後發現as的HEX值為0x00054ae9,我這臺機器數值在內存中為小端存儲,也就是e9 4a 05的醬紫存放.

進入到call add進去看看,是不是內存值也是這:

image.png

恩,實際上就是這樣.

累死了,不寫了。