日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

函數(shù)調用約定(Calling Convention),是一個重要的基礎概念,用來規(guī)定調用者和被調用者是如何傳遞參數(shù)的,既調用者如何將參數(shù)按照什么樣的規(guī)范傳遞給被調用者。

在參數(shù)傳遞中,有兩個很重要的問題必須得到明確說明:

I 當參數(shù)個數(shù)多于一個時,按照什么順序把參數(shù)壓入堆棧;

II 函數(shù)調用后,由誰來把堆棧恢復原狀。

假如在C語言中,定義下面這樣一個函數(shù):

int func(int x,int y, int z)

然后傳遞實參給函數(shù)func()就可以使用了。但是,在系統(tǒng)中,函數(shù)調用中參數(shù)的傳遞卻是一門學問。因為在CPU中,計算機沒有辦法知道一個函數(shù)調用需要多少個、什么樣的參數(shù),也沒有硬件可以保存這些參數(shù)。也就是說,計算機不知道怎么給這個函數(shù)傳遞參數(shù),傳遞參數(shù)的工作必須由函數(shù)調用者和函數(shù)本身來協(xié)調。為此,計算機用棧來支持參數(shù)傳遞。

函數(shù)調用時,調用者依次把參數(shù)壓棧,然后調用函數(shù),函數(shù)被調用以后,在堆棧中取得數(shù)據(jù),并進行計算。函數(shù)計算結束以后,或者調用者、或者函數(shù)本身修改堆棧,使堆棧恢復原狀。

在高級語言中,通過函數(shù)調用約定來說明參數(shù)的入棧和堆棧的恢復問題。常見的調用約定有:

__stdcall
__cdecl
__fastcall
thiscall
__naked call

不同的調用規(guī)約,在參數(shù)的入棧順序,堆棧的恢復,函數(shù)名字的命名上就會不同。在編譯后的代碼量,程序執(zhí)行效率上也會受到影響。

在以上幾種調用約定中,只有__cdecl是可以支持變長參數(shù)的(如支持printf()參數(shù)數(shù)量不確定的函數(shù)),由調用者負責堆棧管理,這兩者是相關的,因為調用者負責傳參,知道參數(shù)的數(shù)量和類型,如果要支持數(shù)量不確定的參數(shù)的話,只能是調用者來管理堆棧平衡。__cdecl也是C語言默認的調用約定。__stdcall每次函數(shù)調用都要由編譯器產生還原堆棧的代碼,所以使用__cdecl方式編譯的程序比使用__stdcall方式編譯的程序要大很多。

C/C++|圖文深入理解函數(shù)調用的5種約定

 

示例代碼:

#include <stdio.h>

int __stdcall stdcallF(int x, int y);    // 被調用函數(shù)自身修改堆棧,WINAPI和CALLBACK

//int       cdeclF(int x ,int y);        // 默認的C調用約定
int __cdecl cdeclF(int x,int y);         // 明確指出C調用約定, 調用者修改堆棧,支持可變參數(shù),比如printf()
int __fastcall fastcallF(int x,int y,int z); // 被調用者修改堆棧

int __declspec(naked) sub(int a,int b)  // 編譯器不會給這種函數(shù)增加初始化和清理代碼,
{                   // 也不能用return語句返回值,只能程序員控制,
                    // 插入?yún)R編返回結果。因此它一般用于驅動程序設計
   __asm mov eax,a
   __asm sub eax,b
   __asm ret
}

class CAdd
{
    int a,b;
public:
    CAdd(int aa,int bb):a(aa),b(bb){}
    int add(int c);  // 參數(shù)從右向左入棧,this指針最后入棧
                     // 參數(shù)個數(shù)不確定時,調用者清理堆棧,否則函數(shù)自己清理堆棧
};

int main()
{
    int a = 3;
    int b = 4;
    int c = 5;
    int s = 0;
    sub(3,4);
    s =  stdcallF(a,b);
    s += cdeclF(a,b);
    s += fastcallF(a,b,c);
    CAdd cadd(3,4);
    s += cadd.add(c);
    printf("%dn",s); //38
    
    getchar();
    return 0;
}
int __stdcall stdcallF(int x, int y)    // WINAPI和CALLBACK
{                                       // 被調用函數(shù)自身修改堆棧
    return x+y;
}

//int       cdeclF(int x ,int y)        // 默認的C調用約定
int __cdecl cdeclF(int x,int y)         // 明確指出C調用約定
{                                       // 調用者修改堆棧,支持可變參數(shù),比如printf()
    return x+y;
}

int __fastcall fastcallF(int x,int y,int z)     // 被調用者修改堆棧
{                   // 函數(shù)的第一個和第二個參數(shù)通過ecx和edx傳遞,剩余參數(shù)從右到左入棧
                    // 在X64平臺,默認使用了fastcall調用約定,因其有較多的寄存器
    return x+y+z;
}

int CAdd::add(int c)    // 參數(shù)從右向左入棧,this指針最后入棧
{                       // 參數(shù)個數(shù)不確定時,調用者清理堆棧,否則函數(shù)自己清理堆棧
    return a+b+c;
}

我們來看一下__cdecl調用的調用者的匯編:

34:       s += cdeclF(a,b);
004010B0   mov         edx,dword ptr [ebp-8]
004010B3   push        edx   // 壓參b
004010B4   mov         eax,dword ptr [ebp-4]
004010B7   push        eax  // 壓參a
004010B8   call        @ILT+10(cdeclF) (0040100f)
004010BD   add         esp,8    // 主調函數(shù)做堆棧平衡,2個int參數(shù),esp偏移8字節(jié)
004010C0   mov         ecx,dword ptr [ebp-10h]
004010C3   add         ecx,eax
004010C5   mov         dword ptr [ebp-10h],ecx   // 返回值

__cdecl調用的被函數(shù)的匯編:

49:   int __cdecl cdeclF(int x,int y)         // 明確指出C調用約定
50:   {                                       // 調用者修改堆棧,支持可變參數(shù),比如printf()
00401230   push        ebp
00401231   mov         ebp,esp
00401233   sub         esp,40h
00401236   push        ebx
00401237   push        esi
00401238   push        edi
00401239   lea         edi,[ebp-40h]
0040123C   mov         ecx,10h
00401241   mov         eax,0CCCCCCCCh
00401246   rep stos    dword ptr [edi]
51:       return x+y;
00401248   mov         eax,dword ptr [ebp+8]
0040124B   add         eax,dword ptr [ebp+0Ch]
52:   }
0040124E   pop         edi
0040124F   pop         esi
00401250   pop         ebx
00401251   mov         esp,ebp
00401253   pop         ebp
00401254   ret    // 這里被調函數(shù)沒有做堆棧平衡

__cdecl代表性的棧示意圖:

C/C++|圖文深入理解函數(shù)調用的5種約定

 

__cdecl代表性的函數(shù)棧幀的示例代碼:

#include <stdio.h>

struct ST{
    int a;
    double d;
    //ST(int aa,double dd):a(aa),d(dd){};
};

ST test(double d,int a)
{
    char ch = 'a';
    char chs[5] = "";
    ST st;
    st.a = a;
    st.d = d;
    return st;
}

int main()
{
    ST s = test(2.3,4);
    printf("%d %fn",s.a,s.d);
    getchar();
    return 0;
}

ref:

http://mallocfree.com/basic/c/c-6-function.htm#79

-End-

分享到:
標簽:函數(shù)
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定