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

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


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

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

/**************************************************************************/
/*add.c*/
int add(int x, int y)
{
return x + y;

return 0;
}
/*************************************************************************/
然后add.h代码为:
/*add.h*/
#ifndef _ADD_H_
#define _ADD_H_

int add(int, int);

#endif
/***************************************************************************/
main函数代码:
/*main.c*/
#include <stdio.h>

int main(void)
{
printf("2+3= %d\n", add(2,3));
return 0;
}
/**********************************************************************************/

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

(1)将add.c做成静态链接库(创建静态库):
首先将add.c编译成目标文件(add.o文件),如下:
#gcc -c add.c             //生成add.o
然后将生成的目标文件(add.o)生成静态库libadd.a:
   #ar crv libadd.a add.o    //生成libadd.a
(2)静态库做好了,就可以在编译main.c时将静态库链接进去了,接下来就编译生成可执行文件(静态库的使用):
#gcc -o exe main.c -I. -L. -ladd  
//或者 #gcc -o exe main.c ./libadd.a
//再或者#gcc -o exe main.c -L. libadd.a

(注:这里的-I/路径, -L/路径, 是通过-I和-L指定对应的库文件名和库文件的路径,这里就是当前目录,
  libadd.a就是要用的静态库,这样对应的静态库已经编译到对应的可执行程序中。执行对应的可执行文件
  便可以得到对应函数调用的结果。在main.c中不需要包含导出文件的头文件。
  上面的(2)分开就是:
  1)编译生成对应的目标文件: 
#gcc -c -I/home/hcj/xxxxxxxx main.c  
  2)生成可执行文件: 
#gcc -o exe -L/home/hcj/xxxxxxxx main.o libstr.a  
还有若主函数是C++程序(即.cpp),则需要在main.cpp中用extern "C"{}包含被调用函数(add.c)的
头文件,编译时用g++编译或者还用gcc编译但需加上一个链接c++库的参数(-lstdc++)
)

(3)最后执行可执行程序:
#./exe

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

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

#gcc -fPIC -c add.c    //生成add.o
#gcc -shared -o libadd.so add.o     /* 或者 #ar crv libadd.so add.o */
(上面两行可以整合成一行:#gcc -fPIC -shared -o libadd.so add.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 -o exe main.c ./libadd.so
  (或者 #gcc -o exe main.c -L. libadd.so
   再或者将libadd.so copy到目录 /usr/lib或/lib中,然后执行:
   #gcc -o exe main.c libadd.so //此时不需要指定搜索路径
  )
   注: 最直接最简单的方法就是把libadd.so拉到/usr/lib或/lib中去。 、
   还有一种方法 export LD_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*/
int add(int x, int y)
{
return x + y;

return 0;
}
/*************************************************************************/
然后add.h代码为:
/*add.h*/
#ifndef _ADD_H_
#define _ADD_H_

int add(int, int);

#endif
/***************************************************************************/
main函数代码:
/*main.c*/
#include <stdio.h>
#include <dlfcn.h>   //显式加载需要用到的头文件

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

int main(void)
{
void *dl;
char *error;
int (*func)();

dl = dlopen(LIB, RTLD_LAZY); /*打开动态链接库*/
if(dl == NULL)
{
printf("Failed load libary\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;
}

return 0;
}
/**********************************************************************************/
2、编译main.c生成可执行程序,动态库的创建已经在上面讲了(动态库显式调用):
1)#gcc -ldl -o exe mian.c
注意要添加-ldl选项,以使用显式调用相关的函数调用。
可以看到,显式调用的代码看上去要复杂很多,但是却比隐式调用要灵活,我们不必在编译时就确定要加载哪个
动态链接库,可以在运行时再确定,甚至重新加载。

2)#./exe






### 回答1: Linux链接动态库是指在程序运行时动态加载库文件,而不是在编译时将库文件静态链接到可执行文件中。动态库可以被多个程序共享,减少了内存的占用,提高了程序的运行效率。在Linux系统中,动态库的文件名通常以.so结尾。程序在运行时需要动态库时,会在系统中查找相应的库文件并加载。动态库使用可以提高程序的可维护性和可扩展性,是Linux系统中常用的一种技术。 ### 回答2: 在Linux系统中,链接动态库是将可执行文件与动态库文件(.so文件)关联起来的过程。动态库是包含一组已编译函数的文件,可以被多个程序共享使用,以减少程序的体积和提高运行效率。 在Linux下,链接动态库有两种方式:隐式链接和显式链接。 隐式链接是在编译源代码时,在编译命令中添加-l选项,指定要链接的动态库名称,编译器会自动在系统库目录中查找并链接对应的动态库。例如,编译命令为: gcc -o test test.c -lm 其中,-lm表示要链接数学库libm.so。隐式链接的优点是方便,无需手动指定动态库的路径,编译器会自动搜索。 显式链接是在程序运行时,通过调用特定的函数来链接动态库。首先,使用dlopen函数打开动态库文件,获取一个句柄;然后,使用dlsym函数根据函数名获取动态库中的符号;最后,使用dlclose函数关闭句柄。这种方式更加灵活,可以在程序运行时动态地加载和卸载动态库,并使用其中的函数。 对于动态库的路径问题,可以使用-L选项指定库文件的搜索路径。例如,编译命令为: gcc -o test test.c -L/path/to/library -lmylib 其中,-L/path/to/library表示动态库所在的路径,-lmylib表示要链接的动态库名称。如果动态库在默认的系统库目录中,可以省略-L选项。 总结一下,Linux链接动态库的方式有隐式链接和显式链接两种,根据需求选择不同的方式。隐式链接通过在编译命令中添加-l选项,自动链接系统库目录中的动态库显式链接使用dlopen、dlsym和dlclose函数,在程序运行时动态加载和卸载动态库。 ### 回答3: 在Linux中,链接动态库是将程序运行所需的动态库文件与程序本身进行关联的过程。在Linux中,动态库文件通常以.so为后缀名。 要链接一个动态库,首先需要使用编译器的选项来指定动态库的路径和名称。一般来说,可以使用-l选项来指定要链接的库文件,例如使用-lm来链接数学库。同时,还要使用-L选项来指定库文件的搜索路径,以确保编译器能够找到库文件。 例如,假设有一个包含以下代码的程序文件main.c: ```c #include <stdio.h> int main() { printf("Hello, World!\n"); return 0; } ``` 如果想要链接数学库libm.so,可以在命令行中执行以下命令进行编译和链接: ``` gcc main.c -o main -lm ``` 这条命令中,gcc是编译器,main.c是源代码文件,-o main指定生成可执行文件的名称为main,-lm指定链接数学库libm.so。 在链接完成后,可以使用ldd命令来查看程序所依赖的动态库,例如执行以下命令: ``` ldd main ``` ldd命令的输出将显示出程序所依赖的动态库文件的路径。 总结来说,链接动态库的过程就是在编译和链接时指定库文件的路径和名称,确保编译器能够找到所需的动态库文件。然后,在运行时,程序会在指定的库文件路径中查找并加载所需的动态库
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值