• <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. CALLBACKWINAPIAFXAPI到底是什么?它們分別在什么地方 被定義的?

            在頭文件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有什么作用?他們的區別是什么?

            a. __stdcall是新標準C/C++函數的調用方法。從底層上說,使用這種調用方法參數的進棧順序和 標準C調用(__cdecl方法)是一樣的,都

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

            b. windows規定,凡事由它來負責調用的函數必須定義為_stdcall類型,比如回調函數、WinMain函數等。

            c. 如果沒有顯試聲明的話,函數的調用方法默認是__cdecl。

             

            3. 調用約定種類

               一共有5中函數調用約定(calling convention),它決定一下內 容:

               1) 函數參數的壓棧順序

               2) 由調用者還是被調用者把參數彈出棧

               3) 產生函數修飾名的方法

             

            __stdcall調用約定:

            函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,

             

            __cdecl調用約定:

            是C和C++程序的缺省調用方式。每一個調用它的函數都包含清空堆棧的代碼, 所以產生的可執行文件大小會比調用__stdcall函數的大。函數采用從右到左的壓棧方式。注意:對于可變參數的成員函數,始終使用__cdecl的轉換方式。

             

            __fastcall調用約定:

            它是通過寄存器來傳送參數的(實際上,它用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在 返回前清理傳送參數的內存棧)。

             

            thiscall調用約定:

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

             

            naked call調用約定:

            采用上述4種調用約 定時,如果必要的話,進入函數時編譯器會產生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數時則產生代碼恢復這些寄存器的內容。naked call不產生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。

             

            關鍵字 __stdcall、__cdecl 和 __fastcall 可以直接加在要輸出的函數前,也可以在編譯環境的 Setting...\C/C++ \Code Generation 項選擇。當加在 輸出函數前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。它們對應的命令行參數分別為/Gz、/Gd 和 /Gr。缺省狀態為/Gd,即__cdecl。缺省狀態為__cdecl。

             

            4. 名 字修飾約定

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

             

            C編譯時函數名修飾約定規則:

            __stdcall調用約定:

                在輸出函數名前加上一 個下劃線前綴,后面加上一個"@"符號 和其參數的字節數,格式為 _functionname@number。

             

            __cdecl調用約定:

                僅在輸出函數名前加上一個下劃線前綴,格式為 _functionname。

             

            __fastcall調用約定:

                在輸出函數名前加上一個"@"符號,后面也是一個"@"符號和其參數的字節數,格式為@functionname@number。

             

            它們均不改變輸出函數名中的字符大小寫。

             

            C++編譯時函數名修飾約定規則:

            __stdcall調用約定:

            以"?"標識 函數名的開始,后跟函數名;函數名后面以"@@YG"標識參數表的開始,后跟參數表;

            參數表以代號表示:

                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——表示指針,后面的代號表明指針類型,如果相同類型的指針連續出現, 以"0"代替,一個"0"代表一次重復;

             

            參數表的第一項為該函數的返回值類型,其后依次為參數的數據類型,指針標識在其所指數據類型前。

            參數表后以"@Z"標識整個名字的結束,如果該函數無參數,則以"Z"標識結束。其格式為

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

                例如

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

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

             

            __cdecl調用約定:

                規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YA"。VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用。

             

            __fastcall調用約定:

                規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YI"。

             

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

            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;

            ......

            };

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

             

            成員函數CopyInfo只有一個參數,是對類CTest的const引用參數,其函數修飾名為“?CopyInfo@CTest@@IAEXABV1@@Z”。

             

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

             

            InsightClass是一個共有的const函數,它的成員函數標識是“@@QBE”,完整的修飾名就是“?InsightClass@CTest@@QBEJK@Z”。

             

            舉例:

            比如動態鏈接庫a有以下導出函數:

            long MakeFun(long lFun);

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

             

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

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

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

            #ifdef _cplusplus

            extern "C" {

            #endif

            long MakeFun(long lFun);

            #ifdef _cplusplus

            }

            #endif

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

             

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

             

            5. 單看函數的名字修飾

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

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

            posted on 2010-04-28 00:10 chatler 閱讀(596) 評論(0)  編輯 收藏 引用 所屬分類: windows
            <2010年8月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            2930311234

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個博客還是不錯,雖然做的東西和我不大相關,覺得看看還是有好處的

            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

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            久久精品国产免费观看| 久久综合九色欧美综合狠狠| 国产精品99久久不卡| 国产精品免费看久久久| 亚洲AV日韩精品久久久久| 女人高潮久久久叫人喷水| 蜜桃麻豆www久久国产精品| 免费精品久久久久久中文字幕| 狠狠人妻久久久久久综合| 精品久久久久久无码人妻热| 久久精品国产一区二区三区不卡| 欧美精品一区二区精品久久| 欧美精品一本久久男人的天堂| 国产精品伊人久久伊人电影| 九九久久精品国产| 久久久久久久亚洲精品| 性做久久久久久久久老女人| 久久精品免费全国观看国产| 亚洲色大成网站WWW久久九九| 久久人人爽人人爽人人AV东京热| 91精品国产综合久久久久久| 久久综合九色综合久99| 久久综合狠狠综合久久97色| 久久精品国产亚洲AV不卡| 久久精品亚洲日本波多野结衣 | 亚洲狠狠婷婷综合久久蜜芽 | 久久AAAA片一区二区| 亚州日韩精品专区久久久| 久久久无码精品亚洲日韩按摩| 国产精品久久久久久搜索| 欧美激情精品久久久久久| 欧美午夜精品久久久久免费视| 国产福利电影一区二区三区久久久久成人精品综合 | 久久水蜜桃亚洲av无码精品麻豆| 久久er热视频在这里精品| 久久久久亚洲国产| 亚洲精品高清久久| 色8久久人人97超碰香蕉987| 国产精品热久久无码av| 精品国际久久久久999波多野| 中文字幕无码久久人妻|