前些天,編程序是用到了很久以前寫的C程序,想把里面的函數(shù)利用起來,連接發(fā)現(xiàn)出現(xiàn)了找不到具體函數(shù)的錯誤:
以下是假設(shè)舊的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++的調(diào)用
/*-----------cpp.cpp--------------*/
#include "c.h"
void main()
{
add(1, 0);
}
這樣編譯會產(chǎn)生錯誤cpp.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z),原因是找不到add的目標模塊
這才令我想起C++重載的函數(shù)命名方式和C函數(shù)的命名方式,讓我們回顧一下:C中函數(shù)編譯后命名會在函數(shù)名前加以"_",比如add函數(shù)編譯成obj文件時的實際命名為_add,而c++命名則不同,為了實現(xiàn)函數(shù)重載同樣的函數(shù)名add因參數(shù)的不同會被編譯成不同的名字
例如
int add(int , int)==>add@@YAHHH@Z,
float add(float , float )==>add@@YAMMM@Z,
以上是VC6的命名方式,不同的編譯器會不同,總之不同的參數(shù)同樣的函數(shù)名將編譯成不同目標名,以便于函數(shù)重載是調(diào)用具體的函數(shù)。
編譯cpp.cpp中編譯器在cpp文件中發(fā)現(xiàn)add(1, 0);的調(diào)用而函數(shù)聲明為extern int add(int x, int y);編譯器就決定去找add@@YAHHH@Z,可惜他找不到,因為C的源文件把extern int add(int x, int y);編譯成_add了;
為了解決這個問題C++采用了extern "C",這就是我們的主題,想要利用以前的C程序庫,那么你就要學(xué)會它,我們可以看以下標準頭文件你會發(fā)現(xiàn),很多頭文件都有以下的結(jié)構(gòu)
#ifndef __H
#define __H
#ifdef __cplusplus
extern "C" {
#endif
extern 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 __cplusplus
extern "C" {
#endif
extern 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函數(shù)編譯成_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);調(diào)用的C風(fēng)格的函數(shù),就會知道去c.obj中找_add(int, int)而不是add@@YAHHH@Z;