• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 297,  comments - 15,  trackbacks - 0

            (VC編譯器下)

             

            1. CALLBACK,WINAPIAFXAPI到底是什么?它們分別在什么地方 被定義的?

            在頭文件windef.h中,CALLBACK, WINAPI, APIENTRY

            ……

            #define CALLBACK  __stdcall

            #define WINAPI         __stdcall

            #define WINAPIV       __cdecl

            #define APIENTRY    WINAPI

            ……

             

            在頭文件AFXVER_.H中,AFXAPI的定義如下:

                ……

               // AFXAPI is used on global public functions

            #ifndef AFXAPI

                    #define AFXAPI __stdcall

            #endif

                ……

             

            2. __stdcall__cdecl有什么作用?他們的區(qū)別是什么?

            a. __stdcall是新標(biāo)準(zhǔn)C/C++函數(shù)的調(diào)用方法。從底層上說(shuō),使用這種調(diào)用方法參數(shù)的進(jìn)棧順序和 標(biāo)準(zhǔn)C調(diào)用(__cdecl方法)是一樣的,都

              是從右到左,但是__stdcall采用自動(dòng)清棧的方式,而__cdecl是手工清棧。

            b. windows規(guī)定,凡事由它來(lái)負(fù)責(zé)調(diào)用的函數(shù)必須定義為_(kāi)stdcall類型,比如回調(diào)函數(shù)、WinMain函數(shù)等。

            c. 如果沒(méi)有顯試聲明的話,函數(shù)的調(diào)用方法默認(rèn)是__cdecl。

             

            3. 調(diào)用約定種類

               一共有5中函數(shù)調(diào)用約定(calling convention),它決定一下內(nèi) 容:

               1) 函數(shù)參數(shù)的壓棧順序

               2) 由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧

               3) 產(chǎn)生函數(shù)修飾名的方法

             

            __stdcall調(diào)用約定:

            函數(shù)的參數(shù)自右向左通過(guò)棧傳遞,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的內(nèi)存棧,

             

            __cdecl調(diào)用約定:

            是C和C++程序的缺省調(diào)用方式。每一個(gè)調(diào)用它的函數(shù)都包含清空堆棧的代碼, 所以產(chǎn)生的可執(zhí)行文件大小會(huì)比調(diào)用__stdcall函數(shù)的大。函數(shù)采用從右到左的壓棧方式。注意:對(duì)于可變參數(shù)的成員函數(shù),始終使用__cdecl的轉(zhuǎn)換方式。

             

            __fastcall調(diào)用約定:

            它是通過(guò)寄存器來(lái)傳送參數(shù)的(實(shí)際上,它用ECX和EDX傳送前兩個(gè)雙字(DWORD)或更小的參數(shù),剩下的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在 返回前清理傳送參數(shù)的內(nèi)存棧)。

             

            thiscall調(diào)用約定:

            僅僅應(yīng)用于"C++"成 員函數(shù)。this指 針存放于CX寄存 器,參數(shù)從右到左壓。thiscall不是關(guān)鍵詞,因此不能被程序員指定。

             

            naked call調(diào)用約定:

            采用上述4種調(diào)用約 定時(shí),如果必要的話,進(jìn)入函數(shù)時(shí)編譯器會(huì)產(chǎn)生代碼來(lái)保存ESI,EDI,EBX,EBP寄存器,退出函數(shù)時(shí)則產(chǎn)生代碼恢復(fù)這些寄存器的內(nèi)容。naked call不產(chǎn)生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。

             

            關(guān)鍵字 __stdcall、__cdecl 和 __fastcall 可以直接加在要輸出的函數(shù)前,也可以在編譯環(huán)境的 Setting...\C/C++ \Code Generation 項(xiàng)選擇。當(dāng)加在 輸出函數(shù)前的關(guān)鍵字與編譯環(huán)境中的選擇不同時(shí),直接加在輸出函數(shù)前的關(guān)鍵字有效。它們對(duì)應(yīng)的命令行參數(shù)分別為/Gz、/Gd 和 /Gr。缺省狀態(tài)為/Gd,即__cdecl。缺省狀態(tài)為_(kāi)_cdecl。

             

            4. 名 字修飾約定

            "C" 或者 "C++" 函數(shù)在內(nèi)部(編譯和鏈接)通過(guò)修飾名識(shí)別。修飾名是編譯器在編譯 函數(shù)定義或者原型時(shí)生成的字符串。有些情況下使用函數(shù)的修飾名是必要的,如在模塊定義文件里頭指定輸出"C++"重載函數(shù)、構(gòu)造函數(shù)、析構(gòu)函數(shù),又如在匯編代碼里調(diào)用"C""或"C++"函數(shù)等。修飾名由函數(shù)名、類名、調(diào)用約定、返回類型、參數(shù)等共同 決定。函數(shù)名修飾約定隨編譯種類(C或C++)和調(diào)用約定的不同而不同,下面分別說(shuō)明。

             

            C編譯時(shí)函數(shù)名修飾約定規(guī)則:

            __stdcall調(diào)用約定:

                在輸出函數(shù)名前加上一 個(gè)下劃線前綴,后面加上一個(gè)"@"符號(hào) 和其參數(shù)的字節(jié)數(shù),格式為 _functionname@number。

             

            __cdecl調(diào)用約定:

                僅在輸出函數(shù)名前加上一個(gè)下劃線前綴,格式為 _functionname。

             

            __fastcall調(diào)用約定:

                在輸出函數(shù)名前加上一個(gè)"@"符號(hào),后面也是一個(gè)"@"符號(hào)和其參數(shù)的字節(jié)數(shù),格式為@functionname@number。

             

            它們均不改變輸出函數(shù)名中的字符大小寫(xiě)。

             

            C++編譯時(shí)函數(shù)名修飾約定規(guī)則:

            __stdcall調(diào)用約定:

            以"?"標(biāo)識(shí) 函數(shù)名的開(kāi)始,后跟函數(shù)名;函數(shù)名后面以"@@YG"標(biāo)識(shí)參數(shù)表的開(kāi)始,后跟參數(shù)表;

            參數(shù)表以代號(hào)表示:

                X——void,

                D——char,

                E——unsigned char,

                F——short,

                H——int,

                I——unsigned int,

                J——long,

                K——unsigned long,

                M——float,

                N——double,

                _N——bool,

                ....

                PA——表示指針,后面的代號(hào)表明指針類型,如果相同類型的指針連續(xù)出現(xiàn), 以"0"代替,一個(gè)"0"代表一次重復(fù);

             

            參數(shù)表的第一項(xiàng)為該函數(shù)的返回值類型,其后依次為參數(shù)的數(shù)據(jù)類型,指針標(biāo)識(shí)在其所指數(shù)據(jù)類型前。

            參數(shù)表后以"@Z"標(biāo)識(shí)整個(gè)名字的結(jié)束,如果該函數(shù)無(wú)參數(shù),則以"Z"標(biāo)識(shí)結(jié)束。其格式為

                "?functionname@@YG*****@Z" 或 "?functionname@@YG*XZ",

                例如

                int Test1(char *var1,unsigned long)    -----“?Test1@@YGHPADK@Z”

                void Test2()              -----“?Test2@@YGXXZ”(第一個(gè)X表示返回類型,第二個(gè)X表示參數(shù) 類型)

             

            __cdecl調(diào)用約定:

                規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開(kāi)始標(biāo)識(shí)由上面的"@@YG"變?yōu)?@@YA"。VC++對(duì)函數(shù)的省缺聲明是"__cedcl",將只能被C/C++調(diào)用。

             

            __fastcall調(diào)用約定:

                規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開(kāi)始標(biāo)識(shí)由上面的"@@YG"變?yōu)?@@YI"。

             

            對(duì)于C++的類成員函數(shù)(其調(diào)用方式是thiscall),函數(shù)的名字修飾與非成員的C++函數(shù)稍有不同,首先就是在函數(shù)名字和參數(shù)表之間插入以“@”字符引導(dǎo)的類名;其次是參數(shù)表的開(kāi)始標(biāo)識(shí)不同,公有(public)成員函數(shù)的標(biāo)識(shí)是“@@QAE”,保護(hù)(protected)成員函數(shù)的標(biāo)識(shí)是“@@IAE”,私有(private)成員函數(shù)的標(biāo)識(shí)是“@@AAE”,如果函數(shù)聲明使用了const關(guān)鍵字,則相應(yīng)的標(biāo)識(shí)應(yīng)分別為“@@QBE”,“@@IBE”和“@@ABE”。如果參數(shù)類型是類實(shí)例的引用,則使用“AAV1”,對(duì)于const類型的引用,則使用“ABV1”。下面就以類CTest為例說(shuō)明C++成員函數(shù)的名字修飾規(guī)則:

            class CTest

            {

            ......

            private:

                void Function(int);

            protected:

                void CopyInfo(const CTest &src);

            public:

                long DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE bUnder, bool bSet);

                long InsightClass(DWORD dwClass) const;

            ......

            };

            對(duì)于成員函數(shù)Function,其函數(shù)修飾名為“?Function@CTest@@AAEXH@Z”,字符串“@@AAE”表示這是一個(gè)私有函數(shù)。“X”表示返回類型為void,“H”表示參數(shù)類型為int類型。

             

            成員函數(shù)CopyInfo只有一個(gè)參數(shù),是對(duì)類CTest的const引用參數(shù),其函數(shù)修飾名為“?CopyInfo@CTest@@IAEXABV1@@Z”。

             

            DrawText是一個(gè)比較復(fù)雜的函數(shù)聲明,不僅有字符串參數(shù),還有結(jié)構(gòu)體參數(shù)和HDC句柄參數(shù),需要指出的是HDC實(shí)際上是一個(gè)HDC__結(jié)構(gòu)類型的指針,這個(gè)參數(shù)的表示就是“PAUHDC__@@”,其完整的函數(shù)修飾名為“?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z”。

             

            InsightClass是一個(gè)共有的const函數(shù),它的成員函數(shù)標(biāo)識(shí)是“@@QBE”,完整的修飾名就是“?InsightClass@CTest@@QBEJK@Z”。

             

            舉例:

            比如動(dòng)態(tài)鏈接庫(kù)a有以下導(dǎo)出函數(shù):

            long MakeFun(long lFun);

            動(dòng)態(tài)庫(kù)生成的時(shí)候采用的函數(shù)調(diào)用約定是__stdcall,所以編譯生成的a.dll中函數(shù)MakeFun的調(diào)用約定是_stdcall,也就是函數(shù)調(diào)用時(shí)參數(shù)從右向左入棧,函數(shù)返回時(shí)自己還原堆棧。現(xiàn)在某個(gè)程序模塊b要引用a中的MakeFun,b和a一樣使用C++方式編譯,只是b模塊的函數(shù)調(diào)用方式是__cdecl,由于b包含了a提供的頭文件中MakeFun函數(shù)聲明,所以MakeFun在b模塊中被 其它調(diào)用MakeFun的函數(shù)認(rèn)為是__cdecl調(diào)用方式,b模塊中的 這些函數(shù)在調(diào)用完MakeFun當(dāng)然要幫著恢復(fù)堆棧啦,可是MakeFun已經(jīng)在結(jié)束時(shí)自己恢復(fù)了堆棧,b模塊中的函數(shù)這樣多此一舉就引起了棧指針錯(cuò)誤,從而引發(fā)堆棧異常。宏觀上的現(xiàn)象就是函數(shù)調(diào)用沒(méi)有問(wèn)題(因?yàn)閰?shù)傳遞 順序是一樣的),MakeFun也完成了自己的功能,只是函數(shù)返回后引發(fā)錯(cuò)誤。解決的方法也很簡(jiǎn)單,只要保證兩個(gè)模塊的在編譯時(shí)設(shè)置相同的函數(shù)調(diào)用約定就行了。

             

            現(xiàn)在再假定兩個(gè)模塊在編譯的時(shí)候都采用__stdcall調(diào)用約定,但是a.dll使用C語(yǔ)言的語(yǔ)法編譯的(C語(yǔ)言方式),所以a.dll的 載入庫(kù)a.lib中MakeFun函數(shù)的名字修飾就是“_MakeFun@4”。b包含了a提供的頭文件中MakeFun函數(shù)聲明,但是由于b采用的是C++語(yǔ)言編譯,所以MakeFun在b模塊中被按照C++的名字修飾規(guī)則命名為“?MakeFun@@YGJJ@Z”,編譯過(guò)程相安無(wú)事,鏈接程序時(shí)c++的鏈接器就到a.lib中去找“?MakeFun@@YGJJ@Z”,但是a.lib中只有“_MakeFun@4”,沒(méi)有“?MakeFun@@YGJJ@Z”,于是鏈接器就報(bào)告:

            error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z

            解決的方法和簡(jiǎn)單,就是要讓b模塊知道這個(gè)函數(shù)是C語(yǔ)言編譯的,extern "C"可以做到這一點(diǎn)。一個(gè)采用C語(yǔ)言編譯的庫(kù)應(yīng)該考慮到使用這個(gè)庫(kù)的程序可能是C++程序(使用C++編譯器),所以在設(shè)計(jì)頭文件時(shí)應(yīng)該注意這一點(diǎn)。通常應(yīng)該這樣聲明 頭文件:

            #ifdef _cplusplus

            extern "C" {

            #endif

            long MakeFun(long lFun);

            #ifdef _cplusplus

            }

            #endif

            這樣C++的編譯器就知道MakeFun的修飾名是“_MakeFun@4”,就不會(huì)有鏈接錯(cuò)誤了。

             

            許多人不明白,為什么我使用的編譯器都是VC的編譯器還會(huì)產(chǎn)生“error LNK2001”錯(cuò)誤?其實(shí),VC的編譯器會(huì)根據(jù)源文件的擴(kuò)展名選擇編譯方式,如果文件的擴(kuò)展名是“.C”,編譯器會(huì)采用C的語(yǔ)法編譯,如果擴(kuò)展名是“.cpp”,編譯器會(huì)使用C++的語(yǔ)法編譯程序,所以,最好的方法就是使用extern "C"。

             

            5. 單看函數(shù)的名字修飾

            有兩種方式可以檢查你的程序中的函數(shù)的名字修飾:使用編譯輸出列表或使用Dumpbin工具。使用/FAc,/FAs或/FAcs命令行參數(shù)可以讓編譯器輸出函數(shù)或變量名字列表。使用dumpbin.exe /SYMBOLS命令也可以獲得obj文件或lib文件中的函數(shù)或變量名字列表。此外,還可以使用 undname.exe 將修飾名轉(zhuǎn)換為未修飾形式。

            from:
            http://patmusing.blog.163.com/blog/static/13583496020103233446784/

            posted on 2010-04-28 00:10 chatler 閱讀(610) 評(píng)論(0)  編輯 收藏 引用 所屬分類: windows
            <2009年10月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺(jué)這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺(jué)得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久婷婷色综合一区二区| 日韩精品久久久久久免费| 国产一区二区三精品久久久无广告| 天天综合久久久网| 久久www免费人成看国产片| 久久婷婷国产剧情内射白浆 | 精品乱码久久久久久久| 2021国产成人精品久久| 久久青青色综合| 99久久国产亚洲高清观看2024| 欧美亚洲国产精品久久高清| 精品久久777| 日产精品久久久一区二区| 久久www免费人成看国产片| 日韩人妻无码精品久久免费一| 免费一级做a爰片久久毛片潮| 久久无码人妻一区二区三区| 人妻中文久久久久| 精品午夜久久福利大片| 色偷偷偷久久伊人大杳蕉| 久久男人中文字幕资源站| 一本大道久久a久久精品综合| 精品久久久无码人妻中文字幕| 久久精品亚洲男人的天堂| 亚洲嫩草影院久久精品| 中文字幕日本人妻久久久免费| 久久久久18| 久久午夜福利电影| 久久久久这里只有精品| 国产精品久久久久久久久久免费| 日韩亚洲欧美久久久www综合网| 欧美精品久久久久久久自慰| 亚洲中文精品久久久久久不卡| 国产精品美女久久福利网站| 精品久久综合1区2区3区激情| 激情综合色综合久久综合| 日本福利片国产午夜久久| 91久久精品电影| 久久人人爽人人澡人人高潮AV| 久久亚洲国产精品五月天婷| 色综合久久88色综合天天 |