Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1350409
  • 博文数量: 281
  • 博客积分: 8800
  • 博客等级: 中将
  • 技术积分: 3346
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-17 22:31
文章分类

全部博文(281)

文章存档

2013年(1)

2012年(18)

2011年(16)

2010年(44)

2009年(86)

2008年(41)

2007年(10)

2006年(65)

我的朋友

分类:

2006-06-11 19:20:58

链接指示符(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

}

 

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

 

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++实现都支持的。

################################################################
如何用c语言调用c++做成的动态链接库
 
 

链接库头文件:
//head.h
class A
{
        public:
        A();
        virtual ~A();
        int gt();
        int pt();
private:
        int s;
};

.cpp
//firstso.cpp
#include
#include "head.h"

A::A(){}
A::~A(){}
int A::gt()
        {
s=10;
                }
int A::pt()
        {
       
                std::cout<        }
编译命令如下:
g++ -shared -o libmy.so firstso.cpp
这时候生成libmy.so文件,将其拷贝到系统库里面:/usr/lib/
进行二次封装:
.cpp
//secso.cpp
#include
#include "head.h"
extern "C"

{

int f();

int f()
{
A a;
a.gt();
a.pt();
return 0;
}

}
编译命令:
gcc -shared -o sec.so secso.cpp -L. -lmy
这时候生成第二个.so文件,此时库从一个类变成了一个c的接口.
拷贝到/usr/lib
下面开始调用:
//test.c
#include "stdio.h"
#include "dlfcn.h"

#define SOFILE "sec.so"
int (*f)();
int main()
{
void *dp;
dp=dlopen(SOFILE,RTLD_LAZY);
f=dlsym(dp,"f");
f();
return 0;
}
编译命令如下:
gcc -rdynamic -s -o myapp test.c
运行Z$./myapp
10
$

阅读(910) | 评论(0) | 转发(0) |
0

上一篇:私有实例构造函数

下一篇:NAS与SAN的解释

给主人留下些什么吧!~~