分享

extern c链接指示符与C和C++接口的建立方法

 昵称16235376 2014-03-13

很久以前写的了,一直忘了贴上来了~今天看ORACLE烦了,有时间贴上来~

前言:

昨天脑子一热问了一个很easy的问题,结果把自己都弄糊涂了,惹来高手们的痴笑,回头想一想是个很简单的问题,却想了那么多,晕!今天做个总结吧!(:---

 

链接指示符(linkage directive) extern “C” 的问题:

 

如果我们在自己的程序中要用到其它语言所编写的函数,那么我们的调用函数就必须告诉编译器使用不同的要求。

因为函数在函数名的生成,参数的入栈,栈的清空等等方面与所使用的语言是密切相关的。

Linkage director 告诉编译器,该函数是用其他的程序设计语言编写的。Linkage director 有两种形式:

1.单一的语句(single statement)形式:

extern “C” void exit(int );

2.复合的语句(compound statement)形式:

extern “C”

{

       int printf(const char *…..);

   int scanf(const char *..);

}

或:

extern “C”

{

#include <cmath>

}

 

这里{  }只是一个分割符,用来说明在那个链接指示符用在那些声明上,没有其它的意义了,也就是说在花括号内声明的函数是可见的了!不要胡思乱想!

 

extern “XX”可以告诉编译器,该函数是用其它语言来编写的,但强制这些函数采用XX语言的方式进行编译。

 

举个例子说吧!

 

C接口的方法:

C中调用C++函数:

生成一个工程叫demo

加入一个demo.c 内容如下:

 

       int    add(int a,int b );//有无都可

 

int main()

{

        int i=add(3,2);

      

       printf("i=%d",i);

       return 0;

}      

再加入一个add.cpp,(注意扩展名,强制控制台生成两种文件类型)内容如下:

 

       int    add(int a,int b)

{    

       return a+b;

}

如果这样编译的话,会产生下面的错误:Linking...

demo.obj : error LNK2001: unresolved external symbol _add

Debug/demo.exe : fatal error LNK1120: 1 unresolved externals

Error executing link.exe

根据提示说是没有找到-add,可是我明明定义了!.

为了搞清这个这个问题,就要搞清楚到底C的函数和CPP的函数在编译之后,会变成什么样子!

       C++支持重载,C中不支持,而C++怎么区分这些重载函数呢,究竟调用那下一个呢?C++会能过不同的参数来选择不同的 具有相同名称的不同的实现函数的调用。C++函数被编译以后,函数会附带一些附加的参数信息!

我们以实事说话:

add.cpp中再加入一个同名函数实现重载

        float   add(float fa,float fb)

       {

              return fa+fb;

       }

       int    add(int a,int b)

{    

       return a+b;

}

单独编译此文件没有问题!

输出一个map文件,找到add函数:

Address         Publics by Value              Rva+Base     Lib:Object

 

 0001:00000030      _main                      00401030 f   demo.obj

 0001:00000080   ?add@@YAMMM@Z              00401080 f   add.OBJ

 0001:000000b0   ?add@@YAHHH@Z              004010b0 f   add.OBJ

明白了吧!

精华都在这里了!

提示说找不到_add,原来是在C语言被编译过程中,自动加上了下画线了!

看看main就知道了!

而在C++中,函数被扩展了,?add@@YAMMM@Z,前面两个@相当于左括号,后面一个相当于右括号,中间的就是用来表示参数的了!简单吧!

两个重载函数使用了不同的参数,一个是整数,一个是浮点数,当这两个函数进行连接时,在相应的C++的连接过程中,编译器也会以编译出来的名字到对应的OBJ文件中查找。在C++编译中,会把参数的类型,名字和函数的名字组合成为一个新的函数,这样就和程序定义的函数名这相一致!

 

现在在看看错误提示,我们的输出文件中也找不到这个_add,所以程序不可能连接上。知道原因就解决吧!

最直接的方法就是修改C++文件的编译结果,让它产生一个对应C的函数名字,也就是按C的方法来生成对应的函数。

C++的头文件中加入:

extern "C" int add(int a,int b);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

extern “C” 语句的作用就是把C++函数按C约定编译!

运行,OK

 

Address         Publics by Value              Rva+Base     Lib:Object

 

 0001:00000030       _main                      00401030 f   demo.obj

 0001:00000080   ?add@@YAMMM@Z              00401080 f   add.obj

 0001:000000b0     _add                       004010b0 f     add.obj

变了吧!

 

C++中调用C呢!

由于两者之间的关系,C++中可以说是直接调用就可以了!不过这是有条件的!

demo.c改成:

int  substract(int a,int b)

{

       return a-b;

}

void hello()

{

       printf("hello");

}

add.cpp改为:

void hello();

int substract(int a,int b);

 

int main()

{

       hello();

       int c=substract(4,1);

       printf("c=%d",c);

       return 0;

}

编译运行,出现了上次一样的错误:

 

加上

extern "C"

{ void hello();

 int substract(int a,int b);

}

 

OK了!

这是两者的map 比较:

 0001:00000030       _substract                 00401030 f   demo.obj

 0001:00000060       _hello                     00401060 f   demo.obj

 

 

0001:00000030       _substract                 00401030 f   demo.obj

 0001:00000060       _hello                     00401060 f   demo.obj

 

发现是一样的!

问题在于你在add.cpp前面加入了:

void hello();

int substract(int a,int b);

这样就把两个函数编译成了C++方式,去找它的实现代码的时候,肯定出错了!

而在C调用C++的时候, int  add(int a,int b );加与不加,系统只是会给出一个提示,

这或许就是C++强的地方吧!

把错误消灭在初期!

 

Extern “C”是唯一被保证由所有的C++实现都支持的。

以上代码在VC60+WINXP(倒版)下调试通过!

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多