我們拿一個簡單的程序來看一看。
在底層彙編之下函數的調用和內存的關係。
#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)
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
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個函數,有,創建,申請,撤銷,摧毀。