Linux静态链接库与动态链接库

一、静态库与动态库基本概念:

Windows中静态连接库为.lib文件,动态链接库为.dll文件,这两种文件在平时生活中使用Windows我们就见过很多回,而今天我们来说说Linux的动态链接库(又称为共享链接库)(.so文件)与静态链接库(.a文件)。有关于环境变量与编译的基本内容,可参照:
Linux环境变量与系统编程学习笔记
Linux环境C语言编译与头文件等知识点小结

1、静态库与共享库:

由于项目在很多时候都比较大,源文件很多,数量十分庞大,可以把代码打包成库文件,提供库文件和头文件即可,静态库与动态库,都是代码的归档文件。

使用静态库时,把静态库的代码复制到目标文件中;
使用动态库时,是将函数的地址(位置)放到目标文件中。

静态库在链接时就已经将库中代码复制到目标文件,而共享库在程序运行时才链接库文件中所需函数。

2、优缺点:

静态库:
优点:只要链接完成,目标文件独立于库文件(静态),并且由于不需要寻址,运行速度较快。
缺点:目标文件过于臃肿,并且不利于代码的修改、扩展和复用。

动态库:
优点:目标文件小,修改、扩展、复用比较方便;
缺点:目标文件必须和共享库同时存在,代码才可以运行,并且运行速度稍慢。

二、静态库的制作与使用:

1、步骤如下:

①、编译库的源程序file.c,得到file.o文件(gcc -c);
②、创建静态库文件:ar -r libfile.a file.o(注:lib为静态库命名规范),libfile.a文件;
③、使用静态库:写好调用方的源程序test.c,只编译不链接(gcc -c)得到test.o文件,将test.o和创建好的静态库文件libfile.a连接;
④、链接的方式有三种:
->直接链接:gcc test.o libfile.a(库文件必须在当前目录下)
->配置环境变量:LIBRARY_PATH,把库文件所在路径(绝对路径相对路径都可以)放入其中(export LIBRARY_PATH=.),
然后:gcc test.o -l file
->gcc test.o -l file -L 库所在路径(推荐使用的方法)
步骤只是个大概思路,不代表全部要这样做。

2、测试:

(1)、测试代码:

/*add.h*/
#ifndef _ADD_H_
#define _ADD_H_
# include<stdio.h>
int add(int, int);
#endif
/*add.c*/
# include<add.h>
int add(int x, int y){
    return (x+y);
}
/*main.c*/
# include<add.h>
int main(void){
    int sum = add(1,5);
    printf("sum = %d\n",sum);
    return 0;
}

(2)、制作静态库

(由于自定义头文件add.h用<>包含,所以要用-I 加当前路径编译):

这里写图片描述

(3)、测试:

测试方法一与方法二:

这里写图片描述

测试方法三:
这里写图片描述

三、动态库创建与使用:

1、步骤如下:

①、库的源程序file.c编译:gcc -c file.c -o test.o
②、生成共享库:gcc -fPIC -shared file.o -o libXX.so
③、使用(链接)共享库的方式与静态库的方式相同
注意:共享库链接之后需要配置环境变量LD_LIBRARY_PATH(export LD_LIBRARY_PATH=.)才能运行成功,否则在链接时第一种方式就不能写gcc test.o libfile.a而必须写成gcc test.o ./libfile.a,但是其他两种方式必须配置环境变量。一般我们都使用第三种链接方式,所以养成库链接完毕就配置环境变量的习惯。

2、测试:

(1)制作动态链接库:

这里写图片描述

(2)链接

(注意链接之前必须配置环境变量,否则无法运行):
这里写图片描述

(3)ldd命令解释为何最初运行a.out会失败:

此外,ldd命令可以查看共享库或者可执行文件的共享库依赖关系,库后面的十六进制是库的地址,环境变量配置的过程,就是为我们自己的共享库(静态库)分配地址的的过程。我们可以用ldd查看a.out可执行文件的共享库依赖关系来解释为什么未配置环境变量之前“export LD_LIBRARY_PATH=.”运行不通过:
这里写图片描述
没有增加当前目录到动态链接库搜索路径环境变量之前,我们可看到,libadd.so没有地址,无法找到。而配置了之后,libadd.so有了地址,就可以不用加路径而运行通过了。

四、动态调用共享库(动态编程):

代码在运行时才知道调用那个函数,执行哪段代码。

1、相关函数:

#include<dlfcn.h>:1)dlopen():打开共享库
(2)dlsym():从打开的共享库文件中获得一个函数,返回函数指针,通过返回的函数指针调用相应的函数。
(3)dlclose():关闭共享库
(4)dlerror():错误处理

void * dlopen(const  char * filename, int flag);
/*
类似open、fopen,文件名filename为库文件名,可带路径;
flag为RTLD_LAZY(延时从硬盘中加载,dlopen时不加载用的时候才会加载)或者RTLD_NOW)(diopen打开时立即加载)
所谓加载就是将代码块从外部存储设备加载到内存,CPU方可处理;返回库文件的首地址
*/

char * dlerror(void);
/*类似于perror,用于观察上一个函数是否出错,出错返回出错信息字符串,未出错返回NULL*/

void * dlsym(void * handle,const char * symbol);
/*handle为dlopen返回的地址,symbol为一个函数名,返回函数指针(函数的地址)*/

int dlclose(void * handle);/*关闭共享库*/

注意:在编译时,必须使用gcc test.c -ldl而不能直接gcc test.c

2、测试:

/*
gcc 测试文件 -l dl/*编译*/
*/
# include<stdio.h>
# include<dlfcn.h>
# include<stdlib.h>

int main(void)
{
    void * handle = dlopen("./libadd.so",RTLD_NOW);/*打开链接库*/

    char * error = dlerror();
    if(error != NULL){/*判断打开是否成功*/
        perror("dlopen");
        exit(EXIT_FAILURE);
    }
    int (*add)(int,int);/*由于dlsym返回值是函数指针类型,所以要定义一个函数指针接收调用的库文件中的函数*/

    add = dlsym(handle, "add");

    int sum = add(2,54);
    printf("sum = %d\n",sum);

    dlclose(handle);
    return 0;
}

结果:
这里写图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值