<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    so true

    心懷未來,開創(chuàng)未來!
    隨筆 - 160, 文章 - 0, 評論 - 40, 引用 - 0
    數(shù)據(jù)加載中……

    windows下的調(diào)用規(guī)范

    先把幾篇不錯的文章貼在這里:
    一個程序(進(jìn)程,應(yīng)用程序?qū)嵗┻\(yùn)行過程占用內(nèi)存包含:代碼區(qū)、全局區(qū)/靜態(tài)區(qū)、常量區(qū)、堆棧、堆。其中前四種的內(nèi)存是在系統(tǒng)為進(jìn)程分配的進(jìn)程地址 空間中,而堆是系統(tǒng)的內(nèi)存,用戶可以通過malloc\new等進(jìn)行申請使用權(quán)。堆棧簡稱:棧,其實(shí)堆棧是用在函數(shù)上,所以也叫“函數(shù)堆棧”,當(dāng)一個函數(shù) 被調(diào)用時,進(jìn)程內(nèi)核對象為其在進(jìn)程的地址空間的堆棧部分進(jìn)行分配一定的棧內(nèi)存給該函數(shù)使用,函數(shù)堆棧用于:

    (1)在進(jìn)入函數(shù)之前,保存“返回地址”和“環(huán)境變量”;返回地址是指該函數(shù)結(jié)束后,剛從哪里繼續(xù)執(zhí)行下去。

    (2)在進(jìn)入函數(shù)之后,保存實(shí)參或?qū)崊⒖截悺⒕植孔兞俊?nbsp;

    下圖是一個函數(shù)堆棧例子:

    點(diǎn)擊查看原始尺寸

    函數(shù)原型:[連接規(guī)范] 函數(shù)類型 [調(diào)用約定] 函數(shù)名 參數(shù)列表 {......}

    調(diào)用約定: 調(diào)用約定是決定函數(shù)實(shí)參或?qū)崊⒖截愡M(jìn)入和退出函數(shù)堆棧的方式以及函數(shù)堆棧釋放的方式,簡單講就是:實(shí)參或?qū)崊⒖截惾霔!⒊鰲!⒑瘮?shù)堆棧釋放的方式。在Win32下有四種:

    (1)__cdecl : 這個是c/c++默認(rèn)的調(diào)用約定,實(shí)參是以參數(shù)列表從右依次向左入棧,出棧相反,函數(shù)堆棧是由調(diào)用方幫忙釋放。主要用在那些帶有可變參數(shù)的函數(shù)上。

    (2)__stdcall : 這個是WIN API的調(diào)用約定,其實(shí)COM接口等只要是申明定義接口都要顯示指定其調(diào)用約定為__stdcall。實(shí)參是以參數(shù)列表從右依次向左入棧,出棧相反,函數(shù) 堆棧是由被調(diào)用方自己釋放。但是若函數(shù)含有可變參數(shù)那么即使顯示指定了__stdcall,編譯器也會自動把其改變成__cdecl。

    (3)_thiscall : 這個是類的非靜態(tài)成員函數(shù)默認(rèn)的調(diào)用約定,其不能用在含有可變參數(shù)的函數(shù)上,否則編譯會出錯,實(shí)參是以參數(shù)列表從右依次向左入棧,出棧相反,函數(shù)堆棧是由 被調(diào)用方自己釋放。但是注意,類非靜態(tài)成員函數(shù)內(nèi)部都隱含有一個this指針,該指針不是存放在函數(shù)堆棧上,而是直接存放在CPU寄存器上。

    (4)__fastcall : 快速調(diào)用,這樣的函數(shù),它們的實(shí)參并不是存放在函數(shù)堆棧上,而是直接存放在CPU寄存器上,所以不存在入棧、出棧、函數(shù)堆棧釋放。


    _stdcall 與 _cdecl 的區(qū)別
    幾乎我們寫的每一個WINDOWS API函數(shù)都是__stdcall類型的,首先,需要了解兩者之間的區(qū)別: WINDOWS的函數(shù)調(diào)用時需要用到棧(STACK,一種先入后出的存儲結(jié)構(gòu))。當(dāng)函數(shù)調(diào)用完成后,棧需要清除,這里就是問題的關(guān)鍵,如何清除??如果我們的函數(shù)使用了_cdecl,那么棧的清除工作是由調(diào)用者。這樣帶來了一個棘手的問題,不同的編譯器產(chǎn)生棧的方式不盡相同,那么調(diào)用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的問題就解決了,函數(shù)自己解決清除工作。所以,在跨(開發(fā))平臺的調(diào)用中,我們都使用__stdcall(雖然有時是以WINAPI的樣子出現(xiàn))。那么為什么還需要_cdecl呢?當(dāng)我們遇到這樣的函數(shù)如 fprintf()它的參數(shù)是可變的,不定長的,被調(diào)用者事先無法知道參數(shù)的長度,事后的清除工作也無法正常的進(jìn)行,因此,這種情況我們只能使用 _cdecl。到這里我們有一個結(jié)論,如果你的程序中沒有涉及可變參數(shù),最好使用__stdcall關(guān)鍵字。

    Microsoft的幾種函數(shù)調(diào)用約定(calling convention)總結(jié)

      在DLL的調(diào)用中,要特別注意調(diào)用約定的不同,在windows中,有__cdecl,__stdcall,__fastcall,thiscall,naked等幾種調(diào)用約定。
    #include <stdio.h>
    int fun();
    int main(int argc, char *argv[])
    {
     
     printf("this is a local variable:%d",fun(2,3,1));
     return 0;
    }
    int fun(int a,int b,int c)
    {
     return a+b+c;
    }
    1:__cdecl
       __cdecl調(diào)用約定是C/C++的默認(rèn)調(diào)用約定,因?yàn)槭怯烧{(diào)用者清理堆棧,所以可以實(shí)現(xiàn)變長參數(shù)。并且因?yàn)橐竺總€函數(shù)
    調(diào)用都包含清理堆棧的代碼,所以其生成的可執(zhí)行文件要比用__stdcall生成的尺寸大,下面是這種調(diào)用約定的特征。
    (1):參數(shù)傳遞由右向左。
    (2):呼叫者清理堆棧。
    (3):C編譯時函數(shù)符號解析是在函數(shù)名前加一個_。
    (4):/Gd選項強(qiáng)迫整個文件按照該調(diào)用約定進(jìn)行編譯。

    下面是windows下的反匯編代碼:
    ;在主函數(shù)中將參數(shù)3,2入棧,然后調(diào)用被解析后的函數(shù)名稱_fun,
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push 1
     push 3
     push 2
     call _fun
    ;將esp加12,進(jìn)行堆棧清理
     add esp, 12
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push ebp
     mov ebp, esp
     mov eax, DWORD PTR _a$[ebp]
     add eax, DWORD PTR _b$[ebp]
     pop ebp
     ret 0
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    2:__stdcall
    __stdcall是用來呼叫Win32 API函數(shù)的一種調(diào)用約定,由被調(diào)用者清理堆棧,所以不能將擁有變長參數(shù)的函數(shù)聲明為此種
    調(diào)用約定。
    (1):參數(shù)傳遞由右向左
    (2):被調(diào)用函數(shù)清理堆棧
    (3):C編譯時函數(shù)符號解析是在函數(shù)名前加_,函數(shù)名稱后面加@,其后加十進(jìn)制表示的參數(shù)所占的字節(jié),例如有:
         int func(int a,int b,int c),解析后為: _func@12
    (4): /Gz選項強(qiáng)迫整個文件按照該調(diào)用約定進(jìn)行編譯。
    (5): 函數(shù)指針聲明方式:typedef BOOL (__stdcall *funcname_ptr)(int a,int b);
    下面是windows下的反匯編代碼:
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push 1
     push 3
     push 2
     call _fun@12
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push ebp
     mov ebp, esp
     mov eax, DWORD PTR _a$[ebp]
     add eax, DWORD PTR _b$[ebp]
     add eax, DWORD PTR _c$[ebp]
     pop ebp
     ret 12
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    3:__fastdcall
      __fastcall指定函數(shù)參數(shù)通過寄存器來傳遞(只是約定,并不是一種保證)
    (1):前兩個DWORD或者字節(jié)數(shù)小于DWORD的參數(shù)通過ECX和EDX寄存器傳遞,其余參數(shù)從右向左進(jìn)行傳遞
    (2):被調(diào)用者清理堆棧。
    (3):C編譯時函數(shù)符號解析是在函數(shù)名前后都加@,在末尾加十進(jìn)制表示的參數(shù)所占的字節(jié),例如:
         int func(int a,int b,int c),解析后為: _@func@12
    (4): /Gr選項強(qiáng)迫整個文件按照該調(diào)用約定進(jìn)行編譯(main函數(shù)除外)。
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push 1
     mov edx, 3
     mov ecx, 2
     call @fun@12
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push ebp
     mov ebp, esp
     sub esp, 8
     mov DWORD PTR _b$[ebp], edx
     mov DWORD PTR _a$[ebp], ecx
     mov eax, DWORD PTR _a$[ebp]
     add eax, DWORD PTR _b$[ebp]
     add eax, DWORD PTR _c$[ebp]
     mov esp, ebp
     pop ebp
     ret 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    4:thiscall:
      thiscall是調(diào)用C++類成員函數(shù)的默認(rèn)調(diào)用約定
    (1):this指針被放置在ECX中
    (2):被呼叫者清理函數(shù)堆棧
    (3):因?yàn)閠hiscall不是關(guān)鍵字,所以不能被顯示應(yīng)用于代碼中
    #include<iostream>
    using namespace std;
    class temp
    {
     public:
      void print(int,int);
    };
    void temp::print(int a,int b)
    {
     printf("the value is %d",a+b);
    }
    int main()
    {
     temp a;
     a.print(1,2);
     return 0;
    }
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push ebp
     mov ebp, esp
     push ecx     ;ecx中存放this指針的值
     mov DWORD PTR _this$[ebp], ecx
     mov eax, DWORD PTR _a$[ebp]
     add eax, DWORD PTR _b$[ebp]
     push eax
     push OFFSET FLAT:$SG8428
     call _printf
     add esp, 8
     mov esp, ebp
     pop ebp
     ret 8
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push 2
     push 1
     lea ecx, DWORD PTR _a$[ebp]
     call ?print@temp@@QAEXHH@Z   ; temp::print
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    5:naked
       這種調(diào)用約定僅僅被用在VxD驅(qū)動程序中。
    (1):參數(shù)傳遞方式為從右向左
    (2):呼叫者清理被調(diào)用函數(shù)堆棧
    #include<stdio.h>
    int NakedCallFunction(int ,int);
    int main()
    {
     NakedCallFunction(2,3);
     return 0;
    }
    __declspec(naked) int NakedCallFunction(int a,int b)
    {
     __asm
     {
      ret
     }
    }
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     push ebp
     mov ebp, esp
     push 3
     push 2
     call ?NakedCallFunction@@YAHHH@Z  ; NakedCallFunction
     add esp, 8
     xor eax, eax
     pop ebp
     ret 0
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Function compile flags: /Odt
    _a$ = 8       ; size = 4
    _b$ = 12      ; size = 4
    ?NakedCallFunction@@YAHHH@Z PROC NEAR   ; NakedCallFunction
     ret 0
    ?NakedCallFunction@@YAHHH@Z ENDP   ; NakedCallFunction
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    6:還有幾種調(diào)用約定,例如:__pascal, __fortran, __syscall只是在相關(guān)開發(fā)工具中使用,并且不被Microsoft
    所支持。
    注:以上函數(shù)符號名解析都以C為標(biāo)準(zhǔn),C++的解析規(guī)則非常復(fù)雜,這里有一個簡要的描述:
    http://www.microsoft.com/china/community/program/originalarticles/techdoc/dll.mspx



    posted on 2010-04-10 12:17 so true 閱讀(300) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 999国内精品永久免费视频| 亚洲欧洲国产成人综合在线观看| 亚洲中文字幕无码亚洲成A人片| 午夜免费福利网站| 国产又黄又爽胸又大免费视频| 亚洲午夜精品久久久久久人妖| 日韩免费视频一区| 99久久免费精品高清特色大片| 亚洲AV无码一区二区三区电影 | 成年黄网站色大免费全看| 亚洲av无码偷拍在线观看| 亚洲熟妇无码AV在线播放| 免费观看成人毛片a片2008| 一级毛片免费一级直接观看| 亚洲妇女水蜜桃av网网站| 亚洲免费在线观看| 成人免费视频网址| 人妻丰满熟妇无码区免费| 在线播放亚洲精品| 亚洲AV成人噜噜无码网站| 久久精品夜色噜噜亚洲A∨| 毛片免费视频观看| 88av免费观看| 精品久久久久久国产免费了| 亚洲日本一线产区和二线产区对比| 亚洲AV中文无码字幕色三| 国产一区二区三区无码免费| 51精品视频免费国产专区| CAOPORN国产精品免费视频| 亚洲精华国产精华精华液网站| 亚洲视频国产精品| 亚洲色精品vr一区二区三区| 免费一级毛片在播放视频| 四虎永久在线精品免费观看视频| 中文字幕免费播放| 四虎一区二区成人免费影院网址| 亚洲成AV人综合在线观看| 水蜜桃亚洲一二三四在线| 亚洲无线一二三四区手机| 国产精品无码一区二区三区免费 | 亚洲综合无码精品一区二区三区|