• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Visual C++中函數調用方式淺探

    發表于:2007-07-14來源:作者:點擊數: 標簽:
    我們知道在進行函數調用時,有幾種調用方法,分為C式,Pascal式。在C和C++中C式調用是缺省的,除非特殊聲明。二者是有區別的,下面我們用實例說明一下: 1. __cdecl :C和C++缺省調用方式 例子: void Input( int m,int n);/*相當于void __cdecl Input(int m,
    我們知道在進行函數調用時,有幾種調用方法,分為C式,Pascal式。在C和C++中C式調用是缺省的,除非特殊聲明。二者是有區別的,下面我們用實例說明一下:


    1. __cdecl :C和C++缺省調用方式
      例子:
    void Input( int &m,int &n);/*相當于void __cdecl Input(int &m,int &n);*/
       以下是相應的匯編代碼:
       00401068   lea         eax,[ebp-8] ;取[ebp-8]地址(ebp-8),存到eax
       0040106B   push        eax         ;然后壓棧
       0040106C   lea         ecx,[ebp-4] ;取[ebp-4]地址(ebp-4),存到ecx
       0040106F   push        ecx         ;然后壓棧
       00401070   call        @ILT+5(Input) (0040100a);然后調用Input函數
       00401075   add         esp,8       ;恢復棧
       
      從以上調用Input函數的過程可以看出:在調用此函數之前,首先壓棧ebp-8,然后壓棧ebp-4,然后調用函數Input,最后Input函數調用結束后,利用esp+8恢復棧。由此可見,在C語言調用中默認的函數修飾_cdecl,由主調用函數進行參數壓棧并且恢復堆棧。
      下面看一下:地址ebp-8和ebp-4是什么?
      在VC的VIEW下選debug windows,然后選Registers,顯示寄存器變量值,然后在選debug windows下面的Memory,輸入ebp-8的值和ebp-4的值(或直接輸入ebp-8和-4),看一下這兩個地址實際存儲的是什么值,實際上是變量 n 的地址(ebp-8),m的地址(ebp-4),由此可以看出:在主調用函數中進行實參的壓棧并且順序是從右到左。另外,由于實參是相應的變量的引用,也證明實際上引用傳遞的是變量的地址(類似指針)。
    總結:在C或C++語言調用中默認的函數修飾_cdecl,由主調用函數進行參數壓棧并且恢復堆棧,實參的壓棧順序是從右到左,最后由主調函數進行堆?;謴?。由于主調用函數管理堆棧,所以可以實現變參函數。另外,命名修飾方法是在函數前加一個下劃線(_).

      2. WINAPI (實際上就是PASCAL,CALLBACK,_stdcall)
      例子:
    void WINAPI Input( int &m,int &n);
    看一下相應調用的匯編代碼:
    00401068   lea         eax,[ebp-8]
    0040106B   push        eax
    0040106C   lea         ecx,[ebp-4]
    0040106F   push        ecx
    00401070   call        @ILT+5(Input) (0040100a)
        從以上調用Input函數的過程可以看出:在調用此函數之前,首先壓棧ebp-8,然后壓棧ebp-4,然后調用函數Input,在調用函數Input之后,沒有相應的堆?;謴凸ぷ?為其它的函數調用,所以我沒有列出)
        下面再列出Input函數本身的匯編代碼:(實際此函數不大,但做匯編例子還是大了些,大家可以只看前和后,中間代碼與此例子無關)

    39: void WINAPI Input( int &m,int &n)
    40:   {
    00401110   push        ebp
    00401111   mov         ebp,esp
    00401113   sub         esp,48h
    00401116   push        ebx
    00401117   push        esi
    00401118   push        edi
    00401119   lea         edi,[ebp-48h]
    0040111C   mov         ecx,12h
    00401121   mov         eax,0CCCCCCCCh
    00401126   rep stos    dword ptr [edi]
    41:       int s,i;
    42:
    43:       while(1)
    00401128   mov         eax,1
    0040112D   test        eax,eax
    0040112F   je          Input+0C1h (004011d1)
    44:       {
    45:       printf("\nPlease input the first number m:");
    00401135   push        offset string "\nPlease input the first number m"... (004260b8)
    0040113A   call        printf (00401530)
    0040113F   add         esp,4
    46:       scanf("%d",&m);
    00401142   mov         ecx,dword ptr [ebp+8]
    00401145   push        ecx
    00401146   push        offset string "%d" (004260b4)
    0040114B   call        scanf (004015f0)
    00401150   add         esp,8
    47:
    48:       if ( m<1 ) continue;
    00401153   mov         edx,dword ptr [ebp+8]
    00401156   cmp         dword ptr [edx],1
    00401159   jge         Input+4Dh (0040115d)
    0040115B   jmp         Input+18h (00401128)
    49:       printf("\nPlease input the first number n:");
    0040115D   push        offset string "\nPlease input the first number n"... (0042608c)
    00401162   call        printf (00401530)
    00401167   add         esp,4
    50:       scanf("%d",&n);
    0040116A   mov         eax,dword ptr [ebp+0Ch]
    0040116D   push        eax
    0040116E   push        offset string "%d" (004260b4)
    00401173   call        scanf (004015f0)
    00401178   add         esp,8
    51:
    52:       if ( n<1 ) continue;
    0040117B   mov         ecx,dword ptr [ebp+0Ch]
    0040117E   cmp         dword ptr [ecx],1
    00401181   jge         Input+75h (00401185)
    00401183   jmp         Input+18h (00401128)
    53:
    54:       for(i=1,s=0;i<=n;i++)
    00401185   mov         dword ptr [ebp-8],1
    0040118C   mov         dword ptr [ebp-4],0
    00401193   jmp         Input+8Eh (0040119e)
    00401195   mov         edx,dword ptr [ebp-8]
    00401198   add         edx,1
    0040119B   mov         dword ptr [ebp-8],edx
    0040119E   mov         eax,dword ptr [ebp+0Ch]
    004011A1   mov         ecx,dword ptr [ebp-8]
    004011A4   cmp         ecx,dword ptr [eax]
    004011A6   jg          Input+0A3h (004011b3)
    55:           s=s+i;
    004011A8   mov         edx,dword ptr [ebp-4]
    004011AB   add         edx,dword ptr [ebp-8]
    004011AE   mov         dword ptr [ebp-4],edx
    004011B1   jmp         Input+85h (00401195)
    56:       if ( m >= s )
    004011B3   mov         eax,dword ptr [ebp+8]
    004011B6   mov         ecx,dword ptr [eax]
    004011B8   cmp         ecx,dword ptr [ebp-4]
    004011BB   jl          Input+0AFh (004011bf)
    57:           break;
    004011BD   jmp         Input+0C1h (004011d1)
    58:       else
    59:           printf(" m < n*(n+1)/2,Please input again!\n");
    004011BF   push        offset string " m < n*(n+1)/2,Please input agai"... (00426060)
    004011C4   call        printf (00401530)
    004011C9   add         esp,4
    60:       }
    004011CC   jmp         Input+18h (00401128)
    61:
    62:   }
    004011D1   pop         edi
    004011D2   pop         esi
    004011D3   pop         ebx
    004011D4   add         esp,48h
    004011D7   cmp         ebp,esp
    004011D9   call        __chkesp (004015b0)
    004011DE   mov         esp,ebp
    004011E0   pop         ebp
    004011E1   ret         8
    最后,我們看到在函數末尾部分,有ret 8,明顯是恢復堆棧,由于在32位C++中,變量地址為4個字節(int也為4個字節),所以彈棧兩個地址即8個字節。
      由此可以看出:在主調用函數中負責壓棧,在被調用函數中負責恢復堆棧。因此不能實現變參函數,因為被調函數不能事先知道彈棧數量,但在主調函數中是可以做到的,因為參數數量由主調函數確定。
      下面再看一下,ebp-8和ebp-4這兩個地址實際存儲的是什么值,ebp-8地址存儲的是n 的值,ebp -4存儲的是m的值。說明也是從右到左壓棧,進行參數傳遞。

       總結:在主調用函數中負責壓棧,在被調用函數中負責彈出堆棧中的參數,并且負責恢復堆棧。因此不能實現變參函數,參數傳遞是從右到左。另外,命名修飾方法是在函數前加一個下劃線(_),在函數名后有符號(@),在@后面緊跟參數列表中的參數所占字節數(10進制),如:void Input(int &m,int &n),被修飾成:_Input@8
       對于大多數api函數以及窗口消息處理函數皆用 CALLBACK ,所以調用前,主調函數會先壓棧,然后api函數自己恢復堆棧。
       
       如:
          push edx
          push edi
          push eax
          push ebx
          call getdlgitemtexta
       你可以想一下,這幾個寄存器中存的都是什么?

    參考:msdn
    例子為在VC6.0下debug模式下的Win32 Console反匯編代碼。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>