C++中_cdecl _stdcall _fastcall _thiscall関数呼び出し要約(照合アセンブリコード)

8629 ワード

まず、Visual Studio 2008でC++コードに対応するアセンブリコードを表示することを学びます.
プログラムにブレークポイントを追加してデバッガを開始すると、ファイルの中央を右クリック=)で逆アセンブリに移動し、アセンブリコードが表示されます.
 
アセンブリコードは//注釈、説明用//***/注釈で、それぞれ基本データ型、配列、ポインタ、参照の4種類のデータ型のパラメータを使用して比較し、コードは以下の通りである.
#include 
using namespace std;

/*C,C++ default style
**arguments passing order:                      from right to left
**who clean stack:                              caller function*/
void __cdecl Cdecl(int a, int b[], int *c, int &d)
{
    cout << endl;
    cout << a++ << "/t" << b << "/t" << c << "/t" << d++ << "/t" << endl;
    for(int i = 0; i < 3; i ++)
    {
        cout << b[i]++ << "/t";
    }
    cout << endl;
    cout << "hello cdecl" << endl;  
}

/*PASCAL,CALLBACK,Win32 API style
**arguments passing order:                      from right to left
**who clean stack:                              callee function*/
void __stdcall Stdcall(int a, int b[], int *c, int &d)
{
    cout << endl;
    cout << a++ << "/t" << b << "/t" << c << "/t" << d++ << "/t" << endl;
    for(int i = 0; i < 3; i ++)
    {
        cout << b[i]++ << "/t";
    }
    cout << endl;
    cout << "hello stdcall" << endl;
}
//0041175A  pop         edi  
//0041175B  pop         esi  
//0041175C  pop         ebx  
//0041175D  add         esp,0C8h 
//00411763  cmp         ebp,esp 
//00411765  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 
//0041176A  mov         esp,ebp 
//0041176C  pop         ebp  
//0041176D  ret         10h                     //clean the stack       

/*A fast call style
**arguments passing order:                      from right to left
**who clean stack:                              callee function*/
void __fastcall Fastcall(int a, int b[], int *c, int &d)
{
    cout << endl;
    cout << a++ << "/t" << b << "/t" << c << "/t" << d++ << "/t" << endl;
    for(int i = 0; i < 3; i ++)
    {
        cout << b[i]++ << "/t";
    }
    cout << endl;
    cout << "hello fastcall" << endl;
}
//004118D2  pop         edi  
//004118D3  pop         esi  
//004118D4  pop         ebx  
//004118D5  add         esp,0E0h 
//004118DB  cmp         ebp,esp 
//004118DD  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 
//004118E2  mov         esp,ebp 
//004118E4  pop         ebp  
//004118E5  ret         8                       //clean the stack    

class E
{
public:

    int m_e;                                    //member variable

    static int m_s;                             //static variable

    E(int e)
    {
        m_e = e;
    }

    /*static function invoke, just like the __cdecl style
    **note:                                     You can add the modifier __thiscall before the static function name, 
                                                but you cannot force the program pass THIS Pointer to static function
    **arguments passing order:                  from right to left
    **who clean stack:                          caller function*/
    static void Staticcall(int a, int b[], int *c, int &d)
    {
        cout << endl;
        m_s ++;
        cout << a++ << "/t" << b << "/t" << c << "/t" << d++ << "/t" << endl;
        for(int i = 0; i < 3; i ++)
        {
            cout << b[i]++ << "/t";
        }
        cout << endl;
        cout << "hello staticcall" << endl;
    }
    //00412284  pop         edi  
    //00412285  pop         esi  
    //00412286  pop         ebx  
    //00412287  add         esp,0DCh 
    //0041228D  cmp         ebp,esp 
    //0041228F  call        @ILT+460(__RTC_CheckEsp) (4111D1h) 
    //00412294  mov         esp,ebp 
    //00412296  pop         ebp  
    //00412297  ret             

    /*(Only for) C++ member functions default style, ECX register contains THIS Pointer secretly
    **note:                                     You can change the modifier to __cdecl or another to change the arguments passing style or who clean stack responsibility,
                                                but you cannot stopping THIS Pointer passing
    **arguments passing order:                  from right to left
    **who clean stack:                          callee function*/
    void __thiscall Thiscall(int a, int b[], int *c, int &d)
    {
        cout << endl;
        cout << a++ << "/t" << b << "/t" << c << "/t" << d++ << "/t" << endl;
        for(int i = 0; i < 3; i ++)
        {
            cout << b[i]++ << "/t";
        }
        cout << endl;
        cout << "hello thiscall" << endl;
    }
    //00411CDF  pop         edi  
    //00411CE0  pop         esi  
    //00411CE1  pop         ebx  
    //00411CE2  add         esp,0D4h 
    //00411CE8  cmp         ebp,esp 
    //00411CEA  call        @ILT+445(__RTC_CheckEsp) (4111C2h) 
    //00411CEF  mov         esp,ebp 
    //00411CF1  pop         ebp  
    //00411CF2  ret         10h  
};

int E::m_s = 0;                                 //Initial static variable, it cannot be initialed in the class, only if it's a const value

int main(int argc, char *argv[])
{
    //data
    int a = 1;                                  //sample data
    int b[] = {1,2,3};                          //array
    int *c = (int *)malloc(sizeof(int));        //pointer
    int d = 2;                                  //for inference
    E e1(1), e2(2);                             //for member/static function 

    cout << &e1 << "/t" << &e2 << endl;
    printf("%p/t%p/n", &e1.m_e, &e2.m_e);
    printf("%p/t%p/n", &E::Thiscall, &E::Staticcall);
    cout << &E::Thiscall << "/t" << &E::Staticcall << endl;
    cout << &e1.m_s << "/t" << &e2.m_s << "/t" << &E::m_s << "/t" << E::m_s << endl;
    cout << "hello main" << endl;

    Cdecl(a, b, c, d);
//00411838  lea         eax,[d]                 //actually pass the address of the data
//0041183B  push        eax  
//0041183C  mov         ecx,dword ptr [c]
//0041183F  push        ecx  
//00411840  lea         edx,[b]
//00411843  push        edx  
//00411844  mov         eax,dword ptr [a]
//00411847  push        eax  
//00411848  call        Cdecl (411032h) 
//0041184D  add         esp,10h                 //clean the stack

    Stdcall(a, b, c, d);
//00411A6F  lea         eax,[d] 
//00411A72  push        eax  
//00411A73  mov         ecx,dword ptr [c] 
//00411A76  push        ecx  
//00411A77  lea         edx,[b] 
//00411A7A  push        edx  
//00411A7B  mov         eax,dword ptr [a] 
//00411A7E  push        eax  
//00411A7F  call        Stdcall (41103Ch) 

    Fastcall(a, b, c, d);
//00411A84  lea         eax,[d] 
//00411A87  push        eax  
//00411A88  mov         ecx,dword ptr [c] 
//00411A8B  push        ecx  
//00411A8C  lea         edx,[b] 
//00411A8F  mov         ecx,dword ptr [a]       //use two registers to pass the last two arguments(so called "fastcall")
//00411A92  call        Fastcall (4110DCh) 
    
    e1.Thiscall(a, b, c, d);
//00411AD2  lea         eax,[d] 
//00411AD5  push        eax  
//00411AD6  mov         ecx,dword ptr [c] 
//00411AD9  push        ecx  
//00411ADA  lea         edx,[b] 
//00411ADD  push        edx  
//00411ADE  mov         eax,dword ptr [a] 
//00411AE1  push        eax  
//00411AE2  lea         ecx,[e]                 //a secret extra parameter
//00411AE5  call        E::Thiscall (41105Fh) 

    E::Staticcall(a, b, c, d);
//00411B04  lea         eax,[d] 
//00411B07  push        eax  
//00411B08  mov         ecx,dword ptr [c] 
//00411B0B  push        ecx  
//00411B0C  lea         edx,[b] 
//00411B0F  push        edx  
//00411B10  mov         eax,dword ptr [a] 
//00411B13  push        eax  
//00411B14  call        E::Staticcall (4110A5h) 
//00411B19  add         esp,10h                 //clean the stack

    return 0;
}

 
まとめ