搜索
bottom↓
回复: 2

关于extern "C"的一些整理,不知道对不对

[复制链接]

出0入0汤圆

发表于 2010-12-1 20:51:18 | 显示全部楼层 |阅读模式
从网上查了一些关于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周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2010-12-2 19:13:49 | 显示全部楼层
好吧,太长了,我自己总结一下上面的内容,大家看看对不对(VC2008还没搞到,等搞到了我再验证,不过也希望大家也验证一下)

        总之:extern "C"是CPP中的内容,在C代码中出现会产生编译错误;extern "C"出现在函数定义中用于通知CPP编译器采用C语言的规则“产生函数名对应的符号”,出现在函数声明中用于通知CPP链接器采用C语言的规则“搜索函数名对应的符号”;CPP调用C函数应该在函数声明中出现extern "C",用以告知CPP编译器和链接器“此函数的符号是根据C语言的规则产生的”,C调用CPP函数应该在函数定义中出现extern "C",用以告知CPP编译器“此函数编译时应该按照C语言的规则产生符号”。

出0入0汤圆

发表于 2010-12-2 21:36:01 | 显示全部楼层
我这儿有vc2008和2010,需要的和我联系.
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-21 01:21

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表