這次依然是從彙編開始分析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; // 加*就是解引用,讀裡面的值, 不加*就只是讀取內存地址

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

恩,實際上就是這樣.
累死了,不寫了。