extern "C"的由来
由于C和C++编译器生成的目标文件中对函数名和全局变量名的修饰规则不同(如C++支持函数重载,而C不支持),为了兼容性,C++标准制定者创造了extern “C”,故该用语只能用于C++编译器,不能用于C编译器。
extern “C”(C必须大写)是连接申明(linkage declaration),申明的结果是在编译后的目标文件中,按照C编译器的命名修饰规则来修饰被extern "C"申明的变量名和函数名。
//file.c
#include <stddef.h>
struct strA
{
float a;
int b;
} bb[3] = {{1.1, 2}, {3.4, 5}, {4.5 ,7}};
int* iptr[100] = {NULL};
double dss = 1001.01;
//extern "C"
int cfunc(float* fa, int ib)
{
return 0;
}
第三次是去除了extern “C前注释符后的编译结果。
由上图可见,gcc和g++对全局变量在目标文件中的命名修饰规则是相同的(据了解,某些编译器如Microsoft Visual VC/C++ 6.0对全局变量修饰规则不同),即extern "C"实际只作用于函数名,全局变量并不需要extern "C"特殊申明。建议按照惯例将全局变量和函数声明都使用extern "C"申明,至少不会出现问题。
extern "C"的目的
使C++编译器按照c编译器的修饰规则来修饰相应的函数名和全局变量名,以便于C和C++混合编程(如c++调用c函数),而不是使用C编译器编译被extern "C"申明的函数体和全局变量,即函数体中依然可以使用cout、class等C++才有的内部名称,甚至可以使用extern "C"申明的函数名在extern "C"作用域外实现函数重载;全局变量的定义仍然遵循C++规范。
extern "C"的用法
一般的将extern “C” 用于函数和全局变量声明部分,以消除c++编译器给目标名添加的特殊命名修饰,建议放在头文件里, 也可以在模块文件中定义处使用。extern “C”{ }作用于花括号内所有声明和定义,如果没有花括号,则只作用于紧跟在后面(同一行或下一行)的一个声明或定义,举例如下:
//单个声明,extern具有双重含义,修饰"C", 同时修饰被声明的对象
//故类型前面不需要也不能再添加extern
extern "C" float R; //声明
//编译警告:初始化和extern同时出现,其他地方再有定义,报错重复定义
extern "C" int a = 0; //定义,忽略extern
extern "C" struct aa{
float a, b;
}strfor_; //声明
extern "C" void c_fun(float*); //声明
extern "C"
void c_fun( float* a) { ... } //定义
//用于多个声明,可以包括函数、变量定义,extern不再具有双重含义
extern "C"
{
#include “test.h” //应用于test.h中的所有声明和定义
float zzz = 234.9; //定义, 无初始值时,也是定义
extern int out1; //声明
extern void c_fun(float*); //声明
void c_fun1( float* a) { ... } //定义, 不建议
}
使用c++预定义宏__cplusplus
编译器和文件扩展名有如下组合
g++ -c file.c
g++ -c file.cpp
gcc -c file.cpp
gcc -c file.c
前三种情况下,都是使用的c++编译器,即预定义了宏__cplusplus ;第四种情况使用的c编译器,即宏 __cplusplus 未定义,且 extern "C"不适用于这种情况。为了适用各种情况,可以利用该宏做如下处理
#ifdef __cplusplus
extern "C" {
#endif
............... //声明和定义, 如果一行搞定,可以去除{ }
#ifdef __cplusplus
}
#endif
对于前三种情况,#ifdef __cplusplus 是多余的;对于第四种情况,如果没有#ifdef __cplusplus,无法通过编译,所以包括extern “C” 都是多余的。当完全确定使用的何种方式时,不使用#ifdef __cplusplus 是可以的;当不确定使用何种编译器时,建议使用#ifdef __cplusplus 一劳永逸。
另外:
1、在头文件中使用extern “C”,必须使用{},即使是单一声明;
2、当混合编程时,假如fortran中定义了a,在C文件中声明时,extern int a_ = 10 或 int a_ = 10,或int a_;都不会报错,前者编译时有警告,三者在链接时都有alignment警告。尽管a和a_指向相同的内存,编译后就是同一个名称a_,但由于c中赋值的“强制性”优先于extern,所以在这三种情况下,都把a_当做(假定)变量定义,由于c和fortran在字节对齐方面的差别,就有了alignment警告。因此最好还是使用extern int a_,明确声明为外部全局变量,并避免了警告和实际执行中可能产生的错误。
部分参考文件
extern “C”的作用详解
在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?----转