extern "C"的用法

文章轉自開源電子論壇:http://www.openedv.com/forum.php?mod=viewthread&tid=7986php

看一些程序的時候總是有 
「#ifdef __cplusplus 
extern "C" { 
#endif」的定義,搞搞清楚是怎麼回事: 

Microsoft-Specific redefined Macros 
__cplusplus Defined for C++ programs only.  
意思是說,若是是C++程序,就使用 
extern "C"{ 
而這個東東,是指在下面的函數不使用的C++的名字修飾,而是用C的 

The following code shows a header file which can be used by C and C++ 

client applications: 
// MyCFuncs.h 
#ifdef __cplusplus 
extern "C" { //only need to export C interface if 
// used by C++ source code 
#endif 

__declspec( dllimport ) void MyCFunc(); 
__declspec( dllimport ) void AnotherCFunc(); 

#ifdef __cplusplus 

#endif 

當咱們想從C++中調用C的庫時,(注,驅動是用C寫的,連new、delete也不能用,鬱悶)不能僅僅說明 一個外部函數,由於調用C函數的編譯代碼和調用C++函數的編譯代碼是不一樣的。若是你僅說明一個外部函數, C++編譯器假定它是 

C++的函數編譯成功了,但當你鏈接時會發現很可愛的錯誤。 
解決的方法就是指定它爲C函數:  
extern "c" 函數描述  
指定一羣函數的話:  
extern "C"{  
n個函數描述  
}  
若是想C和C++混用的話:  
#ifdef _cplusplus  
extern "C"{  
#endif  
n個函數描述  
#ifdef _cplusplus  
}  
#endif 

extern "C"表示編譯生成的內部符號名使用C約定。 

    C++支持函數重載,而C不支持,二者的編譯規則也不同。函數被C++編譯後在符號庫中的名字與C語言的不一樣。例如,假設某個函數的原型爲: void foo( int x, int y ); 該函數被C編譯器編譯後在符號庫中的名字可能爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字(不一樣的編譯器可能生成的名字不一樣,可是都採用了相同的機制,生成的新名字稱爲「mangled name」)。_foo_int_int這樣的名字包含了函數名、函數參數數量及類型信息,C++就是靠這種機制來實現函數重載的。下面以例子說明,如何在C++中使用C的函數, 

或者在C中使用C++的函數。 
//C++引用C函數的例子 
//test.c 
#include <stdio.h> 
void mytest() 

printf("mytest in .c file ok\n"); 

//main.cpp 
extern "C" 

void mytest(); 

int main() 

mytest(); 
return 0; 


//在C中引用C++函數 
在C中引用C++語言中的函數和變量時,C++的函數或變量要聲明在extern "C"{}裏,可是在C語言中不能使用extern "C",不然編譯出錯。 
//test.cpp 
#include <stdio.h> 
extern "C" 

void mytest() 

printf("mytest in .cpp file ok\n"); 


//main.c 
void mytest(); 
int main() 

mytest(); 
return 0; 

//綜合使用 
通常咱們都將函數聲明放在頭文件,當咱們的函數有可能被C或C++使用時,咱們沒法肯定是否要將函數聲明在extern "C"裏,因此,咱們應該添加 
#ifdef __cplusplus 
extern "C" 

#endif 
//函數聲明 
#ifdef __cplusplus 

#endif 
若是咱們注意到,不少頭文件都有這樣的用法,好比string.h,等等。 
//test.h 
#ifdef __cplusplus 
#include <iostream> 
using namespace std; 
extern "C" 

#endif 
void mytest(); 
#ifdef __cplusplus 

#endif 
這樣,能夠將mytest()的實現放在.c或者.cpp文件中,能夠在.c或者.cpp文件中include "test.h"後使用頭文件裏面的函數,而不會出現編譯錯誤。 
//test.c 
#include "test.h" 
void mytest() 

#ifdef __cplusplus 
cout << "cout mytest extern ok " << endl; 
#else 
printf("printf mytest extern ok n"); 
#endif 

//main.cpp 
#include "test.h" 
int main() 

mytest(); 
return 0; 


extern "C" 的用意  
前些天,編程序是用到了好久之前寫的C程序,想把裏面的函數利用起來,鏈接發現出現了找不到具體函數的錯誤: 

如下是假設舊的C程序庫 

C的頭文件 

/*-----------c.h--------------*/#ifndef _C_H_#define _C_H_extern int 

add(int x, int y);#endif 
C的源文件 

/*-----------c.c--------------*/int add(int x, int y){ return x+y;} 
C++的調用 

/*-----------cpp.cpp--------------*/#include "c.h"void main(){ add 

(1, 0);} 
這樣編譯會產生錯誤cpp.obj: error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z),緣由是找不到add的目標模塊這才令我想起C++重載的函數命名方式和C函數的命名方式,讓咱們回顧一下:C中函數編譯後命名會在函數名前加以"_",好比add函數編譯成obj文件時的實際命名爲_add,而c++命名則不一樣,爲了實現函數重載一樣的函數名add因參數的不一樣會被編譯成不一樣的名字 

例如 
int add(int , int)==>add@@YAHHH@Z, 

float add(float , float )==>add@@YAMMM@Z, 

以上是VC6的命名方式,不一樣的編譯器會不一樣,總之不一樣的參數一樣的函數名將編譯成不一樣目標名,以便於函數重載是調用具體的函數。 

編譯cpp.cpp中編譯器在cpp文件中發現add(1, 0);的調用而函數聲明爲extern int add(int x, int y);編譯器就決定去找add@@YAHHH@Z,惋惜他找不到,由於C的源文件把extern int add(int x, int y);編譯成_add了;爲了解決這個問題C++採用了extern "C",這就是咱們的主題,想要利用之前的C程序庫,那麼你就要學會它,咱們能夠看如下標準頭文件你會發現,不少頭文件都有如下的結構 

#ifndef __H#define __H#ifdef __cplusplusextern "C" {#endifextern int f1(int, int);extern int f2(int, int);extern int f3(int, int); 

#ifdef __cplusplus}#endif#endif /*__H*/若是咱們仿製該頭文件能夠獲得 

#ifndef _C_H_#define _C_H_#ifdef __cplusplusextern "C" {#endifextern 

int add(int, int);#ifdef __cplusplus}#endif#endif /* _C_H_ */ 這樣編譯 

/*-----------c.c--------------*/ 
int add(int x, int y){ 
return x+y; 



這時源文件爲*.c,__cplusplus沒有被定義,extern "C" {}這時沒有生效對於C他看到只是extern int add(int, int);add函數編譯成_add(int, int); 

而編譯c++源文件 

/*-----------cpp.cpp--------------*/ 
#include "c.h" 
void main() 

add(1, 0); 

這時源文件爲*.cpp,__cplusplus被定義,對於C++他看到的是extern "C" {extern int add(int, int);}編譯器就會知道 add(1, 0);調用的C風格的函數,就會知道去c.obj中找_add(int, int)而不是add@@YAHHH@Z;這也就爲何DLL中常看見extern "C" {},windows是採用C語言編制他首先要考慮到C能夠正確調用這些DLL,而用戶可能會使用C++而extern "C" {}就會發生做用ios

相關文章
相關標籤/搜索