廢話不多說,請看以下代碼:
我們不妨忽略#define buffer ...這個語句,把buffer看做b代碼如下:
b指向了剛剛開辟的20個位元組大小的記憶體首位址,且b移動的單元為1個位元組(即char類型單元大小)
b[10]=0意思是由b指向的首位址向後移動10*1(b的移動單元)個機關到達的位址,也就是第11個元素所在記憶體的首位址
根據n[10]=0;b[10]!=8和循環中的b[10]++;可知該循環循環8次
b[b[10]]='a'+b[10];這句什麼意思?
循環第一次:b[0]='a'+0
循環第二次:b[1]='a'+1
循環第八次:b[7]='a'+7
我們枚舉下,可知原來是把a~h八個字元分别裝到b[0]~b[7]中
free(b);釋放b所指向的位址,可能問題來了?free如何知道b所指向的空間有20B大小呢?
答:存放在b所指空間大小存放在寄存器ax中,并壓入棧ss中儲存。
(寄存器:cpu中可以儲存資料的器件)
舉個栗子附圖:
我們寫的c代碼如下:
我們使用系統自帶的debug調試一次這個程式
首先,已知main在我的虛拟機中的偏移位址為01fa,這樣我們直接跳至cs:1fa處,
找到main函數中轉化成彙編代碼的c語言代碼。
SUB SP,+02 (彙編指令)等價于 sp+=2; 想必大家已經猜出來了,這正是char *b;其含義就是初始化一個空間給b(姑且可以這樣抽象的了解),而這個空間将放在棧(ss:sp注:括号中不了解沒關系,ss<<2+sp代表的是棧的實體首位址)中,sp是指向棧頂的,是以其+2(為什麼+2?因為b是一個指針,指針大小是2Byte,棧存儲機關是1B,是以指針入棧棧頂需要+2,姑且可以這樣抽象的了解)
之後,MOV AX,0014 (彙編指令)作用等價于AX=20;這裡的0014是16進制,轉化為10進制就是20,即我們malloc的空間大小。
PUSH AX (彙編指令)翻譯過來就是ax的值入棧,把ax的值儲存在棧中,就是儲存malloc配置設定空間size的值。
于是乎,我們證明了free是如何知道malloc所配置設定的空間大小。ax=20;ax入棧;到運作free時在從棧中取出20即可。
簡而言之,這個簡化的程式就是把a~f8個字元寫入申請的b[0]~b[7]中
那麼,重點來了(注意!這裡才是重點)
這裡第一反應就是:Are you kidding me?
你會說為啥變量變成一個數了呢?還是16進制的數。
far:指明後面的對象是一個确切的實體位址(就是根據0x200能直接在記憶體中找到是0x200的實體位址)
第一個* :代表後面的對象是一個記憶體空間
第二個* :代表後面的對象是一個記憶體空間位址
(int far *)0x200:表示這個一個實體位址,該位址的存儲機關是2Byte(int即2Byte,但有些作業系統不同,這裡不多做解釋)
本質是:這個資料是一個位址,一個機關為1Byte的空間的位址。也就是0x200位址空間中裝着一個機關為1Byte的位址,這個位址我們還未确定,因為我們還未初始化。
b=(char *)malloc(20);初始化0x200位址記憶體,即把malloc開辟的首位址裝入0x200中
b即裝在0x200位址記憶體中的資料。
b[10]:裝在0x200位址記憶體中的資料是一個位址,b[10]即這個位址+10Byte(char)位址記憶體中的資料。
這裡來波實戰:
首先,我們寫一個c代碼,這裡的編譯器用的是古老的TC,為什麼要用這麼老的東西?
因為越古老的東西越不存在那些華麗而又繁雜的裝飾,資源配置設定,連結,預編譯特别簡單,便于我們分析,我們甚至可以自己動手寫一個c語言編譯器開發環境。
之後,我們運作完這個程式,檢視0x200位址的記憶體,(圖中0000:0200代表0x200位址)
發現記憶體資料為00 00 ,根據我們前面的推測,記憶體資料00 00 是一個位址,是malloc配置設定空間的首位址。
為什麼隻取前兩個?
malloc此類的函數配置設定的空間都是在棧中,是以,對應的前兩個位元組應該是棧的偏移位址。
于是,我們來到位址00 00 處:
ds:0000可以抽象了解為檢視0000位址處的記憶體。
這裡前面是以16進制表示,後面表示的是16進制轉化為ascll的值
我們可以看到a~h的ascll碼十六進制和其轉化為字元形式,從b~(b+7)位址記憶體中依次存儲(這裡b的位址即00 00)
于是,我們證明了0x200中存的資料是一個位址,且這個位址是malloc的首位址。
最後留一個小彩蛋,那麼b[10]在哪呢?其中的值又是多少?仔細看看00 00位址記憶體的那張圖,你将會得到答案!