內存棧的分配和堆的分配 。printf函數的認識

我們拿一個簡單的程序來看一看。
在底層彙編之下函數的調用和內存的關係。
#include<stdio.h>
int add(int a,int b)
{
 int c=0;
 c=a+b;
 return c;
}
void main()
{
 int a=10;
 int b=20;
 int z=0;
 z=add(a,b);
 printf("%d",z)

    3: {
008E13A0  push        ebp 
008E13A1  mov         ebp,esp
008E13A3  sub         esp,0CCh
008E13A9  push        ebx 
008E13AA  push        esi 
008E13AB  push        edi 
008E13AC  lea         edi,[ebp-0CCh]
008E13B2  mov         ecx,33h
008E13B7  mov         eax,0CCCCCCCCh
008E13BC  rep stos    dword ptr es:[edi]
     4:  int c=0;
008E13BE  mov         dword ptr [c],0
     5:  c=a+b;
008E13C5  mov         eax,dword ptr [a]
008E13C8  add         eax,dword ptr [b]
008E13CB  mov         dword ptr [c],eax
     6:  return c;
008E13CE  mov         eax,dword ptr [c]
     7: }
008E13D1  pop         edi 
008E13D2  pop         esi 
008E13D3  pop         ebx 
008E13D4  mov         esp,ebp
008E13D6  pop         ebp 
008E13D7  ret 


   這是add函數的一部分,我們看出,先是要保留ebp裏面的內容,,然後把一會可能用到的esi,edi,ebx壓入到棧裏面,然後分配給次函數棧幀,大小就是十六進制的cch,對其進行調試信息,將所分配的空間全賦值爲cc,在VC之下我們可能經常看到的是“燙燙燙燙”,這是其對應的中文漢字,但這不是絕對的,有的編譯器會出現“凸凸凸”的情形。隨後計算之後,結果就使用eax返回,
    你可能會問,那結果多於eax的字長那?
如果多餘eax的字長,系統會使eax和ebx兩個寄存器來返回結果,在eax裏面放低位的值,在ebx裏面放高位的值。
然後我們就將原來的寄存器的內容回覆並恢復esp和ebp的內容。
但是,在函數調用後要將其傳入的參數也釋放掉,這就用到了函數的調用慣例,所謂的函數的調用慣例就是,傳入參數的順序,是從左還是從右,釋放時是調用函數釋放,還是函數自己本身釋放。 就比如你要和一個外國人交流,要麼他說你的語言,要麼你說他的語言。
    在c語言裏面的默認調用方式是cdecl,也就是參數從左向右,被調用者釋放的一種慣例。而且其名字修飾是在函數前面加上下劃線。(所謂名字修飾就是爲了在鏈接的時候對調用慣例進行區分)。
     另外還存在着stdcall,此方式的調用不同於上面的是函數自己釋放,等等。
     棧一般的增長方式是向下增長的。
     那爲什麼要引進堆那?
    因爲棧裏面的變量的生存期都受到了函數的影響,調用函數不能對一個已經“死亡”的變量進行應用。但又人會說那我定義個全局變量就行了麼。但全局變量的靈活性太差。  於是引進了堆。
   堆的實現機制,其實就是向運行庫申請一塊內存,然後自己去管理。運行庫就像超市的“物品”一樣,應用進程去“超市”採購,若有,就賣給他,沒有,運行庫就通過API向系統申請更大的內存。
現在大家清楚了 malloc其實就相當一個運行庫,他包裝了4個函數,有,創建,申請,撤銷,摧毀。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章