C++中extern "C"的设立动机是实现C++与C及其他语言的混合编程。
C++为了支持函数的重载。C++对全局函数的处理方式与C有明显的不同。对于函数void foo( int x, int y );该函数被C编译器编译后在符号库中的名字为_foo。而C++编译器则会产生像_foo_int_int之类的名字。
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的keyword。该keyword告诉编译器。其声明的函数和变量能够在本模块或其他模块中使用。
通常。在模块的头文件里对本模块提供给其他模块引用的函数和全局变量以keywordextern声明。
比如,假设模块B欲引用该模块A中定义的全局变量和函数时仅仅需包括模块A的头文件就可以。这样,模块B中调用模块A中的函数时,在编译阶段,模块B尽管找不到该函数。可是并不会报错。它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern相应的keyword是static。被它修饰的全局变量和函数仅仅能在本模块中使用。因此,一个函数或变量仅仅可能被本模块使用时,其不可能被extern “C”修饰。
被extern "C"修饰的变量和函数是依照C语言方式编译和连接的。
比如:如果在C++中:
// 模块A头文件 moduleA.h
int foo( int x, int y );
在模块B中引用该函数:
// moduleB.cpp
#include "moduleA.h"
foo(2,3);
实际上,在连接阶段。连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这种符号!
加extern "C"声明后,模块A的头文件变为:
// 模块A头文件 moduleA.h
extern "C" int foo( int x, int y );
在模块B的实现文件里仍然调用foo( 2,3 ),其结果是:
(1)模块A编译生成foo的目标代码时。没有对其名字进行特殊处理,採用了C语言的方式;
(2)连接器在为模块B的目标代码寻找foo(2,3)调用时。寻找的是未经改动的符号名_foo。
假设在模块A中函数声明了foo为extern "C"类型,而模块B中包括的是extern int foo( int x, int y ) 。则模块B找不到模块A中的函数;反之亦然。
在C语言的头文件里,对其外部函数仅仅能指定为extern类型。C语言中不支持extern "C"声明。在.c文件里包括了extern "C"时会出现编译语法错误。在C中引用C++语言中的函数和变量时。C++的头文件需加入extern "C",可是在C语言中不能直接引用声明了extern "C"的该头文件。应该仅将C文件里将C++中定义的extern "C"函数声明为extern类型。比如:
//C++头文件 a.h
extern "C" int foo( int x, int y );
//C++实现文件 a.cpp
#include "a.h"
int foo( int x, int y ){return x + y;}
/* C实现文件 b.c
这样会编译出错:#include "a.h" */
extern int foo( int x, int y );
int main( int argc, char* argv[] ){
foo( 2, 3 );
return 0;
}