成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

C/C++函數(shù)的編譯方式與調(diào)用約定以及extern“C”的使用

C/C++函數(shù)的編譯方式與調(diào)用約定以及extern “C”的使用,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

創(chuàng)新互聯(lián)是一家網(wǎng)站設(shè)計(jì)、做網(wǎng)站,提供網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),網(wǎng)站制作,建網(wǎng)站,按需策劃設(shè)計(jì),網(wǎng)站開(kāi)發(fā)公司,公司2013年成立是互聯(lián)行業(yè)建設(shè)者,服務(wù)者。以提升客戶(hù)品牌價(jià)值為核心業(yè)務(wù),全程參與項(xiàng)目的網(wǎng)站策劃設(shè)計(jì)制作,前端開(kāi)發(fā),后臺(tái)程序制作以及后期項(xiàng)目運(yùn)營(yíng)并提出專(zhuān)業(yè)建議和思路。

C/C++:函數(shù)的編譯方式與調(diào)用約定以及extern “C”的使用

函數(shù)在C++編譯方式與C編譯方式下的主要不同在于:由于C++引入了函數(shù)重載(overload),因此編譯器對(duì)同名函數(shù)進(jìn)行了名稱(chēng)重整(name mangle)。因此,在C++中引

用其他C函數(shù)庫(kù)時(shí),需要對(duì)聲明使用的函數(shù)做適當(dāng)?shù)奶幚?,以告知編譯器做出適應(yīng)的名稱(chēng)處理。

函數(shù)的調(diào)用約定涉及了函數(shù)參數(shù)的入棧順序、清棧主體(負(fù)責(zé)清理?xiàng)5闹黧w:函數(shù)自身還是調(diào)用函數(shù)者?)、部分名稱(chēng)重整。

如,在C編譯方式下有_stdcall、_cdecl等調(diào)用約定,在C++編譯方式下也有_stdcall、_cedecl等調(diào)用約定。

兩個(gè)復(fù)雜修飾的例子:

extern "C" _declspec(dllexport) int __cdecl Add(int a, int b); //C編譯方式導(dǎo)出_cdecl調(diào)用約定函數(shù)

typedef int (__cdecl*FunPointer)(int a, int b);


1.編譯方式

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

__stdcall調(diào)用約定在輸出函數(shù)名前加上一個(gè)下劃線(xiàn)前綴,后面加上一個(gè)“@”符號(hào)和其參數(shù)的字節(jié)數(shù),格式為_(kāi)functionname@number。 

__cdecl調(diào)用約定僅在輸出函數(shù)名前加上一個(gè)下劃線(xiàn)前綴,格式為_(kāi)functionname。 

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

它們均不改變輸出函數(shù)名中的字符大小寫(xiě),這和pascal調(diào)用約定不同,pascal約定輸出的函數(shù)名無(wú)任何修飾且全部大寫(xiě)。 

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

__stdcall調(diào)用約定:

1、以“?”標(biāo)識(shí)函數(shù)名的開(kāi)始,后跟函數(shù)名;

2、函數(shù)名后面以“@@yg”標(biāo)識(shí)參數(shù)表的開(kāi)始,后跟參數(shù)表;

3、參數(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)表明指針類(lèi)型,如果相同類(lèi)型的指針連續(xù)出現(xiàn),以“0”代替,一個(gè)“0”代表一次重復(fù);

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

5、參數(shù)表后以“@z”標(biāo)識(shí)整個(gè)名字的結(jié)束,如果該函數(shù)無(wú)參數(shù),則以“z”標(biāo)識(shí)結(jié)束。

其格式為“?functionname@@yg*****@z”或“?functionname@@yg*xz”,例如 
int test1-----“?test1@@yghpadk@z” 
void test2-----“?test2@@ygxxz”

__cdecl調(diào)用約定:

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

__fastcall調(diào)用約定:

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

2.調(diào)用約定

調(diào)用約定(Calling Convention)是指在程序設(shè)計(jì)語(yǔ)言中為了實(shí)現(xiàn)函數(shù)調(diào)用而建立的一種協(xié)議。這種協(xié)議規(guī)定了該語(yǔ)言的函數(shù)中的參數(shù)傳送方

式、參數(shù)是否可變和由誰(shuí)來(lái)處理堆棧等問(wèn)題。不同的語(yǔ)言定義了不同的調(diào)用約定。

在C++中,為了允許操作符重載和函數(shù)重載,C++編譯器往往按照某種規(guī)則改寫(xiě)每一個(gè)入口點(diǎn)的符號(hào)名,以便允許同一個(gè)名字(具有不同的參

數(shù)類(lèi)型或者是不同的作用域)有多個(gè)用法,而不會(huì)打破現(xiàn)有的基于C的鏈接器。這項(xiàng)技術(shù)通常被稱(chēng)為名稱(chēng)改編(Name Mangling)或者名稱(chēng)修

飾(Name Decoration)。許多C++編譯器廠(chǎng)商選擇了自己的名稱(chēng)修飾方案。

因此,為了使其它語(yǔ)言編寫(xiě)的模塊(如Visual Basic應(yīng)用程序、Pascal或Fortran的應(yīng)用程序等)可以調(diào)用C/C++編寫(xiě)的DLL的函數(shù),必須使

用正確的調(diào)用約定來(lái)導(dǎo)出函數(shù),并且不要讓編譯器對(duì)要導(dǎo)出的函數(shù)進(jìn)行任何名稱(chēng)修飾。

調(diào)用約定用來(lái):(一)處理決定函數(shù)參數(shù)傳送時(shí)入棧和(二)出棧的順序(由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧),以及(三)編譯器用來(lái)識(shí)別函數(shù)名

稱(chēng)的名稱(chēng)修飾約定等問(wèn)題。

1、__cdecl

__cdecl是C/C++和MFC程序默認(rèn)使用的調(diào)用約定,也可以在函數(shù)聲明時(shí)加上__cdecl關(guān)鍵字來(lái)手工指定。采用__cdecl約定時(shí),函數(shù)參數(shù)按

照從右到左的順序入棧,并且由調(diào)用函數(shù)者把參數(shù)彈出棧以清理堆棧。因此,實(shí)現(xiàn)可變參數(shù)的函數(shù)只能使用該調(diào)用約定。由于每一個(gè)使用

__cdecl約定的函數(shù)都要包含清理堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會(huì)比較大。__cdecl可以寫(xiě)成_cdecl。


2、__stdcall

__stdcall調(diào)用約定用于調(diào)用Win32 API函數(shù)。采用__stdcal約定時(shí),函數(shù)參數(shù)按照從右到左的順序入棧,被調(diào)用的函數(shù)在返回前清理傳送參

數(shù)的棧,函數(shù)參數(shù)個(gè)數(shù)固定。由于函數(shù)體本身知道傳進(jìn)來(lái)的參數(shù)個(gè)數(shù),因此被調(diào)用的函數(shù)可以在返回前用一條ret n指令直接清理傳遞參數(shù)的堆

棧。__stdcall可以寫(xiě)成_stdcall。

3、__fastcall

__fastcall約定用于對(duì)性能要求非常高的場(chǎng)合。__fastcall約定將函數(shù)的從左邊開(kāi)始的兩個(gè)大小不大于4個(gè)字節(jié)(DWORD)的參數(shù)分別放在

ECX和EDX寄存器,其余的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的堆棧。__fastcall可以寫(xiě)成_fastcall。


關(guān)鍵字__cdecl、__stdcall和__fastcall可以直接加在要輸出的函數(shù)前,也可以在編譯環(huán)境的Setting...->C/C++->Code Generation項(xiàng)選

擇。它們對(duì)應(yīng)的命令行參數(shù)分別為/Gd、/Gz和/Gr。缺省狀態(tài)為/Gd,即__cdecl。當(dāng)加在輸出函數(shù)前的關(guān)鍵字與編譯環(huán)境中的選擇不同時(shí),直

接加在輸出函數(shù)前的關(guān)鍵字有效。

3._stdcall與_cdecl調(diào)用約定對(duì)比

在“windef.h”頭文件中可找到:

#define CALLBACK __stdcall

#define WINAPI __stdcall

#define WINAPIV __cdecl

#define APIENTRY WINAPI

#define APIPRIVATE __stdcall

#define PASCAL __stdcall

#define cdecl _cdecl

#ifndef CDECL#define CDECL _cdecl

#endif

幾乎我們寫(xiě)的每一個(gè)WINDOWS API函數(shù)都是__stdcall類(lèi)型的,為什么?

首先,我們談一下兩者之間的區(qū)別:WINDOWS的函數(shù)調(diào)用時(shí)需要用到棧(STACK,一種先入后出的存儲(chǔ)結(jié)構(gòu))。當(dāng)函數(shù)調(diào)用

完成后,棧需要清除,這里就是問(wèn)題的關(guān)鍵,如何清除?如果我們的函數(shù)使用了__cdecl,那么棧的清除工作是由調(diào)用者,用

COM的術(shù)語(yǔ)來(lái)講就是客戶(hù)來(lái)完成的。這樣帶來(lái)了一個(gè)棘手的問(wèn)題,不同的編譯器產(chǎn)生棧的方式不盡相同,那么調(diào)用者能否正常

的完成清除工作呢?答案是不能。如果使用__stdcall,上面的問(wèn)題就解決了,函數(shù)自己解決清除工作。所以,在跨(開(kāi)發(fā))平

臺(tái)的調(diào)用中,我們都使用__stdcall(雖然有時(shí)是以WINAPI的樣子出現(xiàn))。那么為什么還需要_cdecl呢?當(dāng)我們遇到這樣的函

數(shù)如fprintf()它的參數(shù)是可變的,不定長(zhǎng)的,被調(diào)用者事先無(wú)法知道參數(shù)的長(zhǎng)度,事后的清除工作也無(wú)法正常的進(jìn)行,因此,這

種情況我們只能使用_cdecl。

注意:

1、_beginthread需要__cdecl的線(xiàn)程函數(shù)地址,_beginthreadex和CreateThread需要__stdcall的線(xiàn)程函數(shù)地址。

2、一般WIN32的函數(shù)都是__stdcall。而且在Windef.h中有如下的定義:

#define CALLBACK __stdcall

#define WINAPI __stdcall

3、復(fù)雜函數(shù)聲明或指針的修飾符示例:

extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);

typedef int (__cdecl*FunPointer)(int a, int b);

4、extern ”C” 的作用(參考:http://hi.baidu.com/qinfengxiaoyue/item/8bd89e81d1cbeb5226ebd9b4)

為什么標(biāo)準(zhǔn)頭文件都有類(lèi)似以下的結(jié)構(gòu)?

  #ifndef __INCvxWorksh

  #define __INCvxWorksh

  #ifdef __cplusplus

  extern "C" {

  #endif

  /*...*/

  #ifdef __cplusplus

  }

  #endif

  #endif /* __INCvxWorksh */

顯然,頭文件中的編譯宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止該頭文件被重復(fù)引用。

那么

#ifdef __cplusplus

extern "C" {

#endif

#ifdef __cplusplus

}

#endif

的作用又是什么呢?

答:被extern "C" 修飾的變量和函數(shù)是按照C語(yǔ)言方式編譯和連接的;即為實(shí)現(xiàn)C++與C語(yǔ)言的混合編程。

明白了C++中extern "C"的設(shè)立動(dòng)機(jī),我們下面來(lái)具體分析extern "C"通常的使用技巧。

extern "C"的慣用法:

(1)在C++中引用C語(yǔ)言中的函數(shù)和變量,在包含C語(yǔ)言頭文件(假設(shè)為cExample.h)時(shí),需進(jìn)行下列處理:

extern "C"

{

#include "cExample.h"

}

而在C語(yǔ)言的頭文件中,對(duì)其外部函數(shù)只能指定為extern類(lèi)型,C語(yǔ)言中不支持extern "C"聲明,在.c文件中包含了extern "C"時(shí)會(huì)出現(xiàn)編

譯語(yǔ)法錯(cuò)誤。

以C++引用C函數(shù)例子工程中包含的三個(gè)文件的源代碼如下:

  /* c語(yǔ)言頭文件:cExample.h */

  #ifndef C_EXAMPLE_H

  #define C_EXAMPLE_H

  extern int add(int x,int y);

  #endif

  /* c語(yǔ)言實(shí)現(xiàn)文件:cExample.c */

  #include "cExample.h"

  int add( int x, int y )

  {

  return x + y;

  }

 

  // c++實(shí)現(xiàn)文件,調(diào)用add:cppFile.cpp

  extern "C"

  {

  #include "cExample.h"

  }

  int main(int argc, char* argv[])

  {

  add(2,3);

  return 0;

  }

如果C++調(diào)用一個(gè)C語(yǔ)言編寫(xiě)的.DLL時(shí),當(dāng)包括.DLL的頭文件或聲明接口函數(shù)時(shí),應(yīng)加extern "C" { }。

(2)在C中引用C++語(yǔ)言中的函數(shù)和變量時(shí),C++的頭文件中的函數(shù)聲明需添加前綴extern "C",但是在C語(yǔ)言中不能直接引用

已由extern "C"修飾過(guò)的函數(shù)聲明或變量的頭文件(因?yàn)镃編譯方式不支持extern “C” 關(guān)鍵字),應(yīng)該在C中將需要引用的C++

中函數(shù)的聲明為extern類(lèi)型。

以C引用C++函數(shù)例子工程中包含的三個(gè)文件的源代碼如下:

  //C++頭文件 cppExample.h

  #ifndef CPP_EXAMPLE_H

  #define CPP_EXAMPLE_H

  extern "C" int add( int x, int y );

  #endif

  //C++實(shí)現(xiàn)文件 cppExample.cpp

  #include "cppExample.h"

  int add( int x, int y )

  {

  return x + y;

  }

 

  /* C實(shí)現(xiàn)文件 cFile.c

  /* 但這樣會(huì)編譯出錯(cuò):#include "cExample.h",因?yàn)镃編譯不支持extern "C" 關(guān)鍵字 */

  extern int add( int x, int y );

  int main( int argc, char* argv[] )

  {

  add( 2, 3 );

  return 0;

  }


5、MFC提供了一些宏,可以使用AFX_EXT_CLASS來(lái)代替__declspec(DLLexport),并修飾類(lèi)名,從而導(dǎo)出類(lèi),

AFX_API_EXPORT來(lái)修飾函數(shù),AFX_DATA_EXPORT來(lái)修飾變量

AFX_CLASS_IMPORT:__declspec(DLLexport)

AFX_API_IMPORT:__declspec(DLLexport)

AFX_DATA_IMPORT:__declspec(DLLexport)

AFX_CLASS_EXPORT:__declspec(DLLexport)

AFX_API_EXPORT:__declspec(DLLexport)

AFX_DATA_EXPORT:__declspec(DLLexport)

AFX_EXT_CLASS:#ifdef _AFXEXT

AFX_CLASS_EXPORT

#else

AFX_CLASS_IMPORT

6、DLLMain負(fù)責(zé)初始化(Initialization)和結(jié)束(Termination)工作,每當(dāng)一個(gè)新的進(jìn)程或者該進(jìn)程的新的線(xiàn)程訪(fǎng)問(wèn)DLL時(shí),或

者訪(fǎng)問(wèn)DLL的每一個(gè)進(jìn)程或者線(xiàn)程不再使用DLL或者結(jié)束時(shí),都會(huì)調(diào)用DLLMain。但是,使用TerminateProcess或

TerminateThread結(jié)束進(jìn)程或者線(xiàn)程,不會(huì)調(diào)用DLLMain。

7、一個(gè)DLL在內(nèi)存中只有一個(gè)實(shí)例

DLL程序和調(diào)用其輸出函數(shù)的程序的關(guān)系:

1)、DLL與進(jìn)程、線(xiàn)程之間的關(guān)系

DLL模塊被映射到調(diào)用它的進(jìn)程的虛擬地址空間。

DLL使用的內(nèi)存從調(diào)用進(jìn)程的虛擬地址空間分配,只能被該進(jìn)程的線(xiàn)程所訪(fǎng)問(wèn)。

DLL的句柄可以被調(diào)用進(jìn)程使用;調(diào)用進(jìn)程的句柄可以被DLL使用。

DLL可以有自己的數(shù)據(jù)段,但沒(méi)有自己的堆棧,使用調(diào)用進(jìn)程的棧,與調(diào)用它的應(yīng)用程序相同的堆棧模式。

2)、關(guān)于共享數(shù)據(jù)段

DLL定義的全局變量可以被調(diào)用進(jìn)程訪(fǎng)問(wèn);DLL可以訪(fǎng)問(wèn)調(diào)用進(jìn)程的全局?jǐn)?shù)據(jù)。使用同一DLL的每一個(gè)進(jìn)程都有自己的DLL全局

變量實(shí)例。如果多個(gè)線(xiàn)程并發(fā)訪(fǎng)問(wèn)同一變量,則需要使用同步機(jī)制;對(duì)一個(gè)DLL的變量,如果希望每個(gè)使用DLL的線(xiàn)程都有自己

的值,則應(yīng)該使用線(xiàn)程局部存儲(chǔ)(TLS,Thread Local Strorage).

關(guān)于C/C++函數(shù)的編譯方式與調(diào)用約定以及extern “C”的使用問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

網(wǎng)頁(yè)名稱(chēng):C/C++函數(shù)的編譯方式與調(diào)用約定以及extern“C”的使用
網(wǎng)站網(wǎng)址:http://jinyejixie.com/article22/gcepcc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、網(wǎng)站營(yíng)銷(xiāo)軟件開(kāi)發(fā)、App設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
尚义县| 祥云县| 绵阳市| 天镇县| 拉孜县| 定兴县| 永和县| 武义县| 光泽县| 惠水县| 壤塘县| 靖边县| 陇川县| 蚌埠市| 湟源县| 冕宁县| 沁阳市| 兴城市| 务川| 哈巴河县| 江安县| 吉林省| 河源市| 大洼县| 元阳县| 凌海市| 米脂县| 报价| 仪陇县| 上栗县| 隆昌县| 临城县| 达孜县| 和林格尔县| 丰镇市| 来安县| 都安| 台安县| 高碑店市| 广水市| 山东省|