關於“#ifdef __cplusplus” 和 " extern "C" "

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

Microsoft-Specific Predefined 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"

{}就會發生作用

來自: http://hi.baidu.com/diablomt/blog/item/df45828ff343a419b31bbaed.html

發佈了4 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章