代码改变世界

extern "C" 有关问题

2013-06-17 16:22  夜与周公  阅读(647)  评论(0编辑  收藏  举报

  之前帮老板编译一个库的代码,遇到了一些问题,后来发现问题出现在extern "C"语法上。 

  1. C/C++语法extern 关键字

  extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。”----百度百科。值得注意的是extern int a,仅是一个声明, 可以在程序中出现多次,并没有分配存储空间,然而变量a作为全局变量,变量a的定义仅能出现一次。

// A.cpp
int i;
int main()
{
    return 0;
}


// B.cpp
int i = 100;

  编译A.cpp文件,B.cpp文件不会提示有错误,在编译阶段,各个文件中定义的全局变量相互是不透明的,编译A时觉察不到B中也定义了i,同样,编译B时觉察不到A中也定义了i。但是在链接阶段,A与B文件融合在一起,全部变量都存在程序的静态存储区域,因此会有如下报错:

                                 

 

  但是,我们直接使用B.cpp中定义的变量,编译器并没有先见之明,在编译阶段无法知道变量定义在文件中,如下代码:

// A.cpp
//int i;
#include <iostream>

using namespace std;

int main()
{
    cout<<i;    
    return 0;
}

// B.cpp
int i=100;

将会提示:“错误 1 error C2065: “i”: 未声明的标识符 d:\程序\test\extern\a.cpp 9 1 extern”

  为此,关键字"extern",便发挥了它的作用,在A.cpp文件中添加声明:extern int i;

// A.cpp
//int i;
extern int i;
#include <iostream>

using namespace std;

int main()
{
    cout<<i;    
    return 0;
}

// B.cpp
int i=100;

程序正常运行输出:100;

  2. C++ extern "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++引入extern “C”。extern “C”是告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

  3. 一般用法

  经常在项目文件看到的extern “C”的用法:

 #ifdef __cplusplus
 extern "C" {
 #endif
 
 // 代码

 #ifdef __cplusplus
 }
 #endif

  代码部分可以是:调用库函数头文件(ie. #include <c.h>、调用函数声明(ie. extern int add())、一段函数代码实现。需要注意宏__cplusplus,标志着编译器将会把代码按C还是C++语法来解释,目的是这个文件既可以被C调用也可以被C++调用。因为C并没有extern “C”语法,因此如果不加宏编译,编译器会报错的。