https://m.toutiao.com/is/89vUoUv/?=C++ 主調(diào)函數(shù)(caller)調(diào)用被調(diào)函數(shù)(callee),編譯器要考慮兩者的相互獨(dú)立和相互聯(lián)系。 一方面通過傳遞參數(shù)(值或址)和函數(shù)返回(值或址)來在兩段代碼之間建立聯(lián)系。傳址還可以形成副作用。 (C++的引用參數(shù)也是一種傳址,只是編譯器做了自動(dòng)取址和解引用取值的動(dòng)作。) 另一方面主調(diào)函數(shù)和被調(diào)函數(shù)都有各自的函數(shù)棧幀(function frame),但兩者的地址空間是透明的(主調(diào)函數(shù)可以通過被調(diào)函數(shù)棧幀上的地址來間接訪問被調(diào)函數(shù)棧幀上的空間)。 主調(diào)函數(shù)調(diào)用被調(diào)函數(shù)時(shí),對(duì)于參數(shù),不管是傳值還是傳址,都會(huì)有一個(gè)壓棧操作,但壓值和壓址的操作稍有不同(后續(xù)會(huì)從匯編的角度分析),參數(shù)壓棧后,被調(diào)函數(shù)體對(duì)參數(shù)的操作就是對(duì)壓??臻g的引用,當(dāng)然,對(duì)值的引用和址的引用在解析成匯編后也會(huì)不同,后者會(huì)增加一個(gè)解引用(從地址取值)的動(dòng)作。 demo: #include <stdio.h>int __cdecl callee(int *a, int &b, int d) // __cdecl是函數(shù)調(diào)用約定,是略寫時(shí)的默認(rèn)調(diào)用約定{ int t = *a; *a = b*d; b = t*d; return *a*b;}void caller(){ int a=3,b=4,d=2; int c = callee(&a,b,d); // 函數(shù)調(diào)用時(shí)會(huì)傳址或傳址,參數(shù)會(huì)通過壓棧而形成副本機(jī)制 printf('%d\n',c);}int main() // main由操作系統(tǒng)調(diào)用而被執(zhí)行,其它函數(shù)要由另外的函數(shù)去調(diào)用才會(huì)被執(zhí)行, // main函數(shù)通常充當(dāng)被調(diào)函數(shù)的作用 // main函數(shù)內(nèi)定義的變量也是局部變量{ caller(); getchar(); return 0;} demo中的主調(diào)函數(shù)是caller,被調(diào)函數(shù)是callee。 1 調(diào)用約定 調(diào)用約定主要定義主調(diào)函數(shù)和被調(diào)函數(shù)對(duì)于堆棧平衡的分工,參數(shù)壓棧順序規(guī)定等。 2 主調(diào)函數(shù)caller棧幀空間的建立
3 主調(diào)函數(shù)caller局部變量壓棧 11: int a=3,b=4,d=2;004010A8 mov dword ptr [ebp-4],3// 局部變量地址以ebp為基準(zhǔn),向低地址方向增長004010AF mov dword ptr [ebp-8],4004010B6 mov dword ptr [ebp-0Ch],2 4 函數(shù)調(diào)用(實(shí)參壓棧)和返回
注意上述引用傳址和指針傳址使用了相同的匯編代碼。 對(duì)于一個(gè)寄存器可以存下的返回值,通常通過eax返回,對(duì)于浮點(diǎn)數(shù),一般通過浮點(diǎn)棧的寄存器返回,對(duì)于復(fù)合類型,會(huì)在主調(diào)函數(shù)的局部空間規(guī)劃出一塊空間用來存放返回值,這塊空間的首地址會(huì)在壓完參數(shù)后壓在棧幀上。
5 caller call caller @ILT+5(?callee@@YAHPAHAAHH@Z):0040100A jmp callee (00401030) 編譯器會(huì)將返回地址004010CE壓棧,此時(shí)的棧幀空間是: 6 被調(diào)函數(shù)棧幀空間建立
7 callee函數(shù)體對(duì)實(shí)參的引用 4: int t = *a;00401048 mov eax,dword ptr [ebp+8]0040104B mov ecx,dword ptr [eax] // 對(duì)a的解引用并賦值0040104D mov dword ptr [ebp-4],ecx5: *a = b*d;00401050 mov edx,dword ptr [ebp+0Ch]00401053 mov eax,dword ptr [edx] // 對(duì)b的解引用并賦值00401055 imul eax,dword ptr [ebp+10h] // 對(duì)b的值的直接引用00401059 mov ecx,dword ptr [ebp+8]0040105C mov dword ptr [ecx],eax6: b = t*d;0040105E mov edx,dword ptr [ebp-4]00401061 imul edx,dword ptr [ebp+10h]00401065 mov eax,dword ptr [ebp+0Ch]00401068 mov dword ptr [eax],edx7: return *a*b;0040106A mov ecx,dword ptr [ebp+8]0040106D mov edx,dword ptr [ebp+0Ch]00401070 mov eax,dword ptr [ecx]00401072 imul eax,dword ptr [edx]8: } 注意以上匯編對(duì)值的直接引用,對(duì)引用傳遞和指針傳遞的變量先是引用地址,然后通過地址來解引用。 8 被調(diào)函數(shù)負(fù)責(zé)的自己部分的堆棧平衡
-End- |
|