[转]Linux下静态、动态库(隐式、显式调用)的创建和使用及区别

Linux下静态、动态库(隐式、显式调用)的创建和使用及区别

转自http://blog.csdn.net/star_xiong/article/details/17301191

一、静态链接库的创建与使用:

1、编写add.c及main.c代码:

/**************************************************************************/

/*add.c*/

intadd(intx,inty)

{

returnx+y;

return0;

}

/*************************************************************************/

然后add.h代码为:

/*add.h*/

#ifndef_ADD_H_

#define_ADD_H_

intadd(int,int);

#endif

/***************************************************************************/

main函数代码:

/*main.c*/

#include<stdio.h>

intmain(void)

{

printf("2+3=%d\n",add(2,3));

return0;

}

/**********************************************************************************/

2、现在首先要明确我们目的是将add.c做成静态链接库,然后main.c调用生成的静态链接库中的add()

(1)将add.c做成静态链接库(创建静态库):

首先将add.c编译成目标文件(add.o文件),如下:

#gcc-cadd.c//生成add.o

然后将生成的目标文件(add.o)生成静态库libadd.a:

#arcrvlibadd.aadd.o//生成libadd.a

(2)静态库做好了,就可以在编译main.c时将静态库链接进去了,接下来就编译生成可执行文件(静态库的使用):

#gcc-oexemain.c

-I.-L.-ladd

//或者#gcc-oexemain.c./libadd.a

//再或者#gcc-oexemain.c-L.libadd.a

(注:这里的-I/路径,-L/路径,是通过-I和-L指定对应的库文件名和库文件的路径,这里就是当前目录,

libadd.a就是要用的静态库,这样对应的静态库已经编译到对应的可执行程序中。执行对应的可执行文件

便可以得到对应函数调用的结果。在main.c中不需要包含导出文件的头文件。

上面的(2)分开就是:

1)编译生成对应的目标文件:

#gcc-c-I/home/hcj/xxxxxxxxmain.c

2)生成可执行文件:

#gcc-oexe-L/home/hcj/xxxxxxxxmain.olibstr.a

还有若主函数是C++程序(即.cpp),则需要在main.cpp中用extern"C"{}包含被调用函数(add.c)的

头文件,编译时用g++编译或者还用gcc编译但需加上一个链接c++库的参数(-lstdc++)

)

(3)最后执行可执行程序:

#./exe

二、动态链接库的创建与使用:

1、把add.c编译生成动态库(创建动态库):

#gcc-fPIC-cadd.c//生成add.o

#gcc-shared-olibadd.soadd.o/*或者#arcrvlibadd.soadd.o*/

(上面两行可以整合成一行:#gcc-fPIC-shared-olibadd.soadd.c)

注:-fpic使输出的对象模块是按照可重定位地址方式生成的(即与位置无关)。

-shared指定把对应的源文件生成对应的动态链接库文件libstr.so文件

2、动态库的使用(动态链接库分:隐式调用和显式调用2种):

(1)隐式调用:

动态链接库(隐式调用)在代码上与写静态链接库没什么区别,主要是在编译时。

代码编写与静态库一样,不需要包含导出函数的头文件,若主函数是C++程序(即.cpp),则需要在main.cpp中用

extern"C"{}包含被调用函数(add.c)的头文件(这里需要包含头文件是与.cpp和.c混合编译有关,同静态\动态库

无关),编译时用g++编译或者还用gcc编译但需加上一个链接c++库的参数(-lstdc++)

1)代码编写:与静态库一样

2)编译main.c生成可执行程序(动态库隐式调用的使用):

#gcc-oexemain.c./libadd.so

(或者#gcc-oexemain.c-L.libadd.so

再或者将libadd.socopy到目录/usr/lib或/lib中,然后执行:

#gcc-oexemain.clibadd.so//此时不需要指定搜索路径

)

注:最直接最简单的方法就是把libadd.so拉到/usr/lib或/lib中去。、

还有一种方法exportLD_LIBRARY_PATH=$(pwd)

另外还可以在/etc/ld.so.conf文件里加入我们生成的库的目录,然后执行#/sbin/ldconfig。

/etc/ld.so.conf是非常重要的一个目录,里面存放的是链接器和加载器搜索共享库时要检查的目录,

默认是从/usr/lib/lib中读取的,所以想要顺利运行,我们也可以把我们库的目录加入到这个文件中

并执行/sbin/ldconfig。另外还有个文件需要了解/etc/ld.so.cache,里面保存了常用的动态函数

库,且会先把他们加载到内存中,因为内存的访问速度远远大于硬盘的访问速度,这样可以提高软件加载

动态函数库的速度了。

3)#./exe

(2)显式调用:

显式调用的动态库的创建与隐式调用相同。(隐式调用与静态库的使用方法一样,不需要包含导出函数的头文件(显式调用

也不用包含头文件),只需要在编译可执行程序时指定库文件的路径)

显式调用和隐式调用的区别在于:编译可执行程序时需要指定库文件的搜索路径,而显式调用编译可执行程序时不用加上

动态库的搜索路径(因为已经在主函数中包含了库文件的路径),但是需要增加几个系统调用:

(#include<dlfcn.h>,//头文件

1)dlopen()

第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。

-环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。

-文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。

-目录usr/lib。

-目录/lib。

-当前目录。

第二个参数:指定如何打开共享库。

-RTLD_NOW:将共享库中的所有函数加载到内存

-RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数

返回值:返回动态库的句柄

2)dlsym()

调用dlsym时,利用dlopen()返回的共享库的句柄以及函数名称作为参数,返回要加载函数的入口地址。

3)dlclose()

关闭动态链接库

4)dlerror()

该函数用于检查调用共享库的相关函数出现的错误。

如果dlerror返回值不为空,则dlsym执行出错

)

1、编写add.c及main.c代码:

/**************************************************************************/

/*add.c*/

intadd(intx,inty)

{

returnx+y;

return0;

}

/*************************************************************************/

然后add.h代码为:

/*add.h*/

#ifndef_ADD_H_

#define_ADD_H_

intadd(int,int);

#endif

/***************************************************************************/

main函数代码:

/*main.c*/

#include<stdio.h>

#include<dlfcn.h>//显式加载需要用到的头文件

#defineLIB"./libadd.so"//指定动态库路径

intmain(void)

{

void*dl;

char*error;

int(*func)();

dl=dlopen(LIB,RTLD_LAZY);/*打开动态链接库*/

if(dl==NULL)

{

printf("Failedloadlibary\n");

}

error=dlerror();/*检测错误*/

if(error!=NULL)

{

printf("%s\n",error);

return-1;

}

func=dlsym(dl,"test");/*获取函数的地址*/

error=dlerror();/*检测错误*/

if(error!=NULL)

{

printf("%s\n",error);

return-1;

}

func();/*调用动态库中函数*/

dlclose(dl);/*关闭共享库*/

error=dlerror();/*检测错误*/

if(error!=NULL)

{

printf("%s\n",error);

return-1;

}

return0;

}

/**********************************************************************************/

2、编译main.c生成可执行程序,动态库的创建已经在上面讲了(动态库显式调用):

1)#gcc-ldl-oexemian.c

注意要添加-ldl选项,以使用显式调用相关的函数调用。

可以看到,显式调用的代码看上去要复杂很多,但是却比隐式调用要灵活,我们不必在编译时就确定要加载哪个

动态链接库,可以在运行时再确定,甚至重新加载。

2)#./exe

相关推荐