|
从网上查了一些关于extern "C"的文章,根据自己的理解整理了一下,但不是很确定对不对——但自我感觉这样的话好像比较说的通,希望大家讨论讨论,验证一下(我明天装上VC2008之后才有条件验证),一下是我的理解:
C与CPP间的互调用
在语法层面,C++语言比C语言多了不少全新的特性,比如:函数重载,面向对象。为了支持这些新特性,C++在对函数的编译和链接时所做的处理与C语言差别很大。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。
相较于C编译器产生的符号_foo,C++产生的符号_foo_int_int不仅包含了函数名,而且同时包含函数参数个数及函数参数类型这两个额外信息,C++就是靠这种机制来实现函数重载的。 同样地,C++中用户所编写程序的类成员变量可能与全局变量同名,在源码中我们以"."来区分。而实际上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,进而对源码中不同域中的同名变量加以区分。
由此可见,C语言和C++之间函数不能直接相互调用,因为对于一个函数名(在源代码形式上看),调用者语言搜索和链接的“符号”与被调用者语言所编译生成的“符号”是不同的。所以,C和CPP间的相互调用必须进行必要的处理才可以正常完成。
由于CPP在设计时首先考虑到的就是与C语言的兼容性,所以,CPP编译器和链接器也可以使用C语言规则来处理函数名,明白这一点至关重要,因为这是实现C和CPP间函数相互调用的关键所在。
而extern "C"就是用来让我们在源码层面上指定CPP所使用的函数名处理(符号产生和搜索)规则的:在函数定义中使用extern "C",则告诉CPP编译器此函数要使用C语言的编译规则产生函数名符号(即采用C语言编译器的规则产生符号),从而使C语言代码可以调用此函数;在函数声明中使用extern "C",则告诉CPP链接器函数是被按照C语言的规则进行符号生成的(有可能就是被C编译器编译的,也有可能是被使用了C编译规则的CPP编译器编译的),所以CPP链接器应该根据相应的规则来寻找函数名所对应的符号。
根据上面的内容还可以很容易的推断,在C语言代码中不能出现extern "C"这样的内容(不管是函数定义中还是函数声明中)原因很简单,extern "C"出现的原因是:CPP可以使用两套不同的规则进行函数名的编译连接处理,而extern "C"就像一个二值开关一样来让我们从两种规则中进行选择,而C语言只用一套编译连接规则,所以根本不需要这个符号。所以请记住,extern "C"这种语法完全是CPP中自己发明和添加的,并不是从C中带出来的。
一、CPP调用C
首先明确,C的内容CPP基本上是完全包含的,CPP不能调用C函数唯一的原因在于CPP链接器寻找的符号与C编译器生成的符号不一致。
所以,处理的方法是:在CPP源文件的函数声明中使用extern "C"
具体语法是在CPP文件中调用C函数的前面进行如下声明:
extern "C" void c_func(void);
如果某个C文件c_file.c中有多个函数被CPP调用,每一个都单独进行上面的声明显得啰嗦,可以这样统一处理:首先对这些函数在c_file.h中进行常规的声明:void c_func(void);然后在调用这些函数的CPP源文件中进行如下包含:
extern "C"
{
#include "c_func.h"
}
但这还不是最好的处理方法,最好的方法是,在c_file.h中这样写:
#ifndef __C_FILE_H__
#define __C_FILE_H__
#ifdef __cplusplus
extern "C" {
#endif
...................................
...................................
#ifdef __cplusplus
}
#endif
#endif //__C_FILE_H__
然后,在CPP源文件中只需进行常规的#include包含即可,当然了,你必须在CPP构建命令的某个环节中给出__cplusplus宏定义,不然上面的extern "C"就根本没写如CPP源文件中。
上面这种做法虽然方便,而且也确实是很多规范的函数库中的应用方法,但却存在一个潜在的弊端:这种书写方法让很多人认为extern "C"是C语言中的语言要素,从而导致对这个语法的认识产生困难。
二、C调用CPP
处理的方法是:首先,在CPP源文件的函数定义中使用extern "C";其次,在C语言源文件中进行不加extern "C"的向前声明。如:
// cpp_file.cpp:
extern "C" void cpp_func(int i)
{
// ...
}
//c_file.c
void cpp_func(int);
void c_func(int i)
{
cpp_func(i);
}
当然了,如果你要在CPP代码中调用此函数的话,你不得不在向前声明中使用extern "C"——这的确很绕人。
当然了,如果你真的明白extern "C"的功能的话,你应该很容明白,对于CPP中的重载函数和类成员函数而言,你不能使用extern "C"进行修饰。所以,你也就不能通过上面的方法来让C代码调用他们,解决的办法就是对上面的内容就行“包装”:在CPP中为重载函数或类成员函数专门定义一个符合C语言规则的函数,在此函数中完成对重载函数或类成员函数的调用,并且使用extern "C"修饰该函数,从而是该函数可以被C代码调用。 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|