linux下静态库动态库的编译及使用

为什么要使用库

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

库的使用可以使程序模块化。

以C++为例子,库分为静态库和动态库,所谓的静态和动态指的是库链接的方式。

  • 静态库(.a、.lib):顾名思义就是在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系。在linux下以.a为后缀,在win下面以.lib为后缀。
  • 动态库(.so、.dll):动态库就是在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。在linux下以.so为后缀,在win下面以.dll为后缀。

image

如何使用静态库

以linux 下使用举例:

Linux通常把库文件存放在/usr/lib或/lib目录下。

静态库的创建和使用:

  1. 在一个头文件种声明静态库所导出的函数。
  2. 在一个源文件种实现静态库所导出的函数。
  3. 编译源文件,生成可执行代码。
  4. 将可执行代码所在的目标文件加入到某个静态库中,为了方便调用,不修改环境变量,可将静态库拷贝到系统默认的存放库文件的目录下。

下面通过一个例子来说明:

  • mylib.h:存放的是静态库提供给用户使用的函数的声明
  • mylib.c:实现了mylib.h种声明的函数

头文件:mylib.h

1
2
3
4
5
6
7
8
#ifndef _MYLIB_H_
#define _MYLIB_H_
#include "stdio.h"

void weclome(void);
void outString(const char *str);

#endif

源文件:mylib.c

1
2
3
4
5
6
7
8
9
10
11
#include "mylib.h"

void welcome(void)
{
printf("welcome to libmylib\n");
}
void outString(const char *str)
{
if(str != NULL)
printf("%s\n", str);
}

静态库编译步骤

  1. 编译mylib.c生成目标文件:
    gcc -o mylib.o -c mylib.c

image

  1. 将目标文件加入到静态库中:
    ar rcs libmylib.a mylib.o

image

  1. 将静态库copy到Linux的库目录(/usr/lib或者/lib)下(此举是为了不修改环境变量,方便测试链接):
    cp libmylib.a /usr/lib/libmylib.a

静态库的链接

  1. 编写调用库函数的测试程序test.c
1
2
3
4
5
6
7
8
#include "stdio.h"
#include "mylib.h"

void main()
{
welcome();
outString("successful");
}
  1. 使用静态库编译:gcc -o test test.c -lmylib

这里注意,编译时无需带上前缀和后缀。

如果没有执行上个的3步骤,没有将静态库拷贝到/usr/lib,而是。

则可以使用以下命令:

Text
1
gcc -o test test.c -lmylib -L ./

image

  1. 运行可执行程序
Text
1
./test

image

静态库编译说明

在Linxu下,可以使用ar命令来创建和修改静态库。

这些在linux下man ar 一下就可以得到参数,这里说明几个常用的:

  • d:从库中删除成员文件
  • r:在库中加入成员文件,若存在,则替换
  • c:创建一个库
  • s:无论ar命令是否修改了库内容,都强制重新生成库符号表

其他的命令用时再man。

  • -L:表示要连接的库所在目录
  • -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a或.so来确定库的名称。

如何使用动态库

依旧以linux 下使用举例:

依旧用上述的三个文件,mylib.c,mylib.h,test.c来做例子。

动态库的编译

  1. 编译mylib.c生成目标文件(-fPIC):

gcc -fPIC -o mylib.o -c mylib.c

image

  1. 将目标文件加入到动态库中:
    gcc -shared -o libmylib.so mylib.o

image

  1. 将动态库copy到Linux的库目录(/usr/lib或者/lib)下(此举是为了不修改环境变量,方便测试链接):
    cp libmylib.so /usr/lib/libmylib.so

当然1,2两个步骤可以合二为一:

gcc -fPIC -shared -o libmylib.so mylib.c

切记:不要写成 gcc -fPIC -shared -o libmylib.so -c mylib.c

在c文件后面不要加上-c。不然在执行可执行文件的时候会出现以下错误:

./Test: error while loading shared libraries: /lib/libmylib.so: only ET_DYN and ET_EXEC can be loaded

动态库的链接

  1. 依旧采用上述的test.c
  2. 使用动态链接库编译可执行程序

gcc -o Test test.c -lmylib -L./

  1. 运行可执行程序: ./Test

注意这里会有错误:

./Test: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

是因为,当前的目录不在linux检索的动态库目录范围内。

修改方法:

  1. 通过修改动态库链接路径:
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/test/so_lib
  2. 直接把库拷贝进入系统默认的路径:/usr/lib/或者/lib/,执行ldconfig。

LD_LIBRARY_PATH: 动态库的查找路径方式

修改当前的动态库路径

  1. 方法一:export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/XXX 但是登出后就失效
  2. 方法二:修改./bash_profile或系统级别的/etc/profile
    • 在其中添加例如export PATH=/opt/ActiveP/lib:$LD_LIBRARY_PATH
    • source .bashrc (Source命令也称为“点命令”,也就是一个点符号.)
    • source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录)
  3. 方法三:这个没有修改LD_LIBRARY_PATH但是效果是一样的实现动态库的查找
    • /etc/ld.so.conf下面加一行/usr/local/mysql/lib
    • 保存过后ldconfig一下。

      ldconfig 命令的用途:

      主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录。
      搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件。
      缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。