图片 1

并用实际的C代码演示了这两种库的生成过程,库又有静态库和动态库之分

在其实的软件开辟项目中,不是每一行代码都亟待我们切身写。在我们的软件出品中,有局地代码(特别是函数卡塔尔(قطر‎的现身频率极高,它们能够被用作公共代码来反复使用。为了幸免重复劳动,大家就把那个集体代码编写翻译为库文件,供应和须求要的程序调用。在Linux中,库分为静态库和动态库两种。

Linux静态库与动态库实例详整

依附链接时期的不等,库又有静态库和动态库之分。静态库是在链接阶段被链接的,所以生成的可实践文件就不受库的震慑,就算库被删除,程序照旧得以成功运行。而动态库是在程序实行的时候被链接的。程序推行完,库仍需保存在系统上,以供程序运行时调用。链接静态库从某种意义上的话是一种复制粘贴,被链接后库就间接嵌入可执路程序中了,那样系统空间有不小的浪费,並且只要发觉系统中有bug,就非得逐项把链接该库的顺序寻觅来,然后再度编译,十二分忙碌。而动态库恰好弥补了那个毛病,因为动态库是在程序运维时被链接的,所以磁盘上只需保存一份别本,二回节约了半空中,若是发掘bug或许是要升迁,只要用新的库把原来的替换掉就足以了。静态库是还是不是荒谬了吧?非也。假使代码在任何系统上运营,且并未有对景挂画的库时,消除办法就是应用静态库。而且由于动态库是在程序运营的时候被链接,因而动态库的运营速度相当慢。

正文对静态库和动态库举办了详尽的介绍,并用实际的C代码演示了那三种库的生成进度。

  1. Linux 下静态链接库编写翻译与使用

  好了,我们精通了关于动态库和静态库的连锁知识,那么什么样使用GCC生成静态库和动态库呢?

图片 1

率先编写如下代码:

  大家参照他事他说加以考察了《LinuxC程序设计大全》下面的例子,来计算GCC下编译静态及其动态链接库的法门及步骤。

一、静态库和动态库简要介绍

明显,程序日常须要通过预管理、编写翻译、汇编和链接那多少个步骤技能形成可实施的程序。在实际上的软件开垦中,对于部分急需被超级多模块一再使用的公物代码,大家就将它们编写翻译为库文件。

库是一种可实施代码的二进制方式,能够被操作系统载入内部存款和储蓄器实践。Linux扶持的库分为静态库和动态库,动态库又称分享库。平日说来,Linux中的一些主要的库是存放在lib目录下的。

静态库文件的后缀为.a,在Linux下平日命名叫libxxx.a。在链接步骤中,连接器将从静态库文件中得到所需的代码,复制到生成的可实践文件中。因而,整个库中的全体函数都被编写翻译进了指标代码中。

动态库文件的后缀为.so,在Linux下平常命名字为libxxx.so。相对于静态库,动态库在编写翻译的时候并未被编写翻译进目的代码中,而是程序实施到相关函数时才调用库中对应的函数。

能够看出,静态库的独特之处是编写翻译后的实施顺序不须要外表的函数库扶持,劣势是借使静态函数库改造了,那么您的次第必得另行编写翻译;而动态库在多少个应用程序都要采用同一函数库的时候就极度相符,但前提是前后相继的运营情状中必需提供相应的库。
不管是静态库,依旧动态库,都以由*.o指标文件生成的。

// main.c
#include "test.h"
int main(){
  test();
  return 0;  
}

// test.h
#include<iostream>
using namespace std;
void test();

// test.c
#include "test.h"
void test(){
 cout<< "test!" <<endl;
}

  程序项目清单如下:

二、静态库生成示例

接下来编写翻译:

  test.c

1.单个文件生成静态库示例

我们编辑如下轻松的四个程序文件:test.h、test.c和main.c,在main.c中要调用test.c中落实的函数test。
test.h文件内容:

#include <stdio.h>

void test();

test.c文件内容:

#include "test.h"

void test()
{
    printf("this is in test....../n");
}

main.c文件内容:

#include "test.h"

int main()
{
    test();
    return 0;
}

将此多少个公文上传到Linux机器上,编写翻译生成静态库文件,之后调用库文件的全体经过如下所示:

~/zhouzhaoxiong/zzx/mytest/a/single> ll
-rw------- 1 zhou dba 53 Nov  4 16:04 main.c
-rw------- 1 zhou dba 80 Nov  4 16:04 test.c
-rw------- 1 zhou dba 36 Nov  4 16:04 test.h
~/zhouzhaoxiong/zzx/mytest/a/single> gcc -c test.c
~/zhouzhaoxiong/zzx/mytest/a/single> ll
-rw------- 1 zhou dba   53 Nov  4 16:04 main.c
-rw------- 1 zhou dba   80 Nov  4 16:04 test.c
-rw------- 1 zhou dba   36 Nov  4 16:04 test.h
-rw-rw-rw- 1 zhou dba 1624 Nov  4 16:06 test.o
~/zhouzhaoxiong/zzx/mytest/a/single> ar -r libtest.a test.o
ar: creating libtest.a
~/zhouzhaoxiong/zzx/mytest/a/single> ll
-rw------- 1 zhou dba   53 Nov  4 16:04 main.c
-rw-rw-rw- 1 zhou dba 1766 Nov  4 16:06 libtest.a
-rw------- 1 zhou dba   80 Nov  4 16:04 test.c
-rw------- 1 zhou dba   36 Nov  4 16:04 test.h
-rw-rw-rw- 1 zhou dba 1624 Nov  4 16:06 test.o
~/zhouzhaoxiong/zzx/mytest/a/single> gcc -o test main.c libtest.a
~/zhouzhaoxiong/zzx/mytest/a/single> ll
-rw------- 1 zhou dba    52 Nov  4 16:09 main.c
-rwxrwxrwx 1 zhou dba 11876 Nov  4 16:09 test
-rw-rw-rw- 1 zhou dba  1766 Nov  4 16:06 libtest.a
-rw------- 1 zhou dba    80 Nov  4 16:04 test.c
-rw------- 1 zhou dba    36 Nov  4 16:04 test.h
-rw-rw-rw- 1 zhou dba  1624 Nov  4 16:06 test.o
~/zhouzhaoxiong/zzx/mytest/a/single> ./test
this is in test......

大家得以看看,生成库文件的吩咐是“ar -r libtest.a
test.o”,而将静态库文件编写翻译进代码的一声令下是“gcc -o test main.c
libtest.a”。

这么生成了静态库文件libtest.a之后,如若还恐怕有别的程序要调用test.c中贯彻的函数,只须要将test.h和libtest.a拷贝到对应的代码工程中,然后施行相近“gcc
-o test main.c libtest.a”那样的下令就可以。

  1. gcc -c test.c //生成靶子文件
  2. ar crv libtest.a test.o //生成静态链接库libtest.a
  3. g++ -o main main.c -ltest //编写翻译main程序同一时候链接libtest.a静态库
  4. ./main //运行main程序

  5. Linux 下动态链接库编写翻译与行使

  int add(int a,int b)

2.七个文本生成静态库示例

我们编辑如下轻松的三个程序文件:test.h、test_1.c、test_2.c、test_3.c和main.c,在main.c中要调用test_1.c、test_2.c、test_3.c中完毕的函数test_1、test_2、test_3。

test.h文件内容:

#include <stdio.h>

void test_1();
void test_2();
void test_3();

test_1.c文书内容:

#include "test.h"

void test_1()
{
    printf("this is in test_1....../n");
}

test_2.c文本内容:

#include "test.h"

void test_2()
{
    printf("this is in test_2....../n");
}

test_3.c文书内容:

#include "test.h"

void test_3()
{
    printf("this is in test_3....../n");
}

main.c文件内容:

#include "test.h"

int main()
{
    test_1();
    test_2();
    test_3();
    return 0;
}

将此五个文件上传到Linux机器上,编写翻译生成静态库文件,之后调用库文件的整套经过如下所示:

~/zhouzhaoxiong/zzx/mytest/a/more> ll
-rw------- 1 zxin10 dba 96 Nov  4 16:11 main.c
-rw------- 1 zxin10 dba 70 Nov  4 16:04 test.h
-rw------- 1 zxin10 dba 84 Nov  4 16:04 test_1.c
-rw------- 1 zxin10 dba 84 Nov  4 16:04 test_2.c
-rw------- 1 zxin10 dba 84 Nov  4 16:04 test_3.c
~/zhouzhaoxiong/zzx/mytest/a/more> gcc -c test_1.c test_2.c test_3.c
~/zhouzhaoxiong/zzx/mytest/a/more> ll
-rw------- 1 zxin10 dba   96 Nov  4 16:11 main.c
-rw------- 1 zxin10 dba   70 Nov  4 16:04 test.h
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_1.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_1.o
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_2.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_2.o
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_3.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_3.o
~/zhouzhaoxiong/zzx/mytest/a/more> ar -r libtest.a test_1.o test_2.o test_3.o
ar: creating libtest.a
~/zhouzhaoxiong/zzx/mytest/a/more> ll
-rw------- 1 zxin10 dba   96 Nov  4 16:11 main.c
-rw-rw-rw- 1 zxin10 dba 5158 Nov  4 16:15 libtest.a
-rw------- 1 zxin10 dba   70 Nov  4 16:04 test.h
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_1.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_1.o
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_2.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_2.o
-rw------- 1 zxin10 dba   84 Nov  4 16:04 test_3.c
-rw-rw-rw- 1 zxin10 dba 1624 Nov  4 16:15 test_3.o
~/zhouzhaoxiong/zzx/mytest/a/more> gcc -o test main.c libtest.a
~/zhouzhaoxiong/zzx/mytest/a/more> ll
-rw------- 1 zxin10 dba    96 Nov  4 16:11 main.c
-rwxrwxrwx 1 zxin10 dba 12008 Nov  4 16:16 test
-rw-rw-rw- 1 zxin10 dba  5158 Nov  4 16:15 libtest.a
-rw------- 1 zxin10 dba    70 Nov  4 16:04 test.h
-rw------- 1 zxin10 dba    84 Nov  4 16:04 test_1.c
-rw-rw-rw- 1 zxin10 dba  1624 Nov  4 16:15 test_1.o
-rw------- 1 zxin10 dba    84 Nov  4 16:04 test_2.c
-rw-rw-rw- 1 zxin10 dba  1624 Nov  4 16:15 test_2.o
-rw------- 1 zxin10 dba    84 Nov  4 16:04 test_3.c
-rw-rw-rw- 1 zxin10 dba  1624 Nov  4 16:15 test_3.o
~/zhouzhaoxiong/zzx/mytest/a/more> ./test
this is in test_1......
this is in test_2......
this is in test_3......

作者们能够见到,生成静态库文件的指令是“ar -r libtest.a test_1.o test_2.o
test_3.o”,而将静态库文件编写翻译进代码的吩咐是“gcc -o test main.c
libtest.a”。

那般生成了静态库文件libtest.a之后,假若还应该有其它程序要调用test_1.c、test_2.c、test_3.c中完结的函数,只须要将test.h和libtest.a拷贝到对应的代码工程中,然后实践相似“gcc
-o test main.c libtest.a”这样的命令就可以。

代码与上述相近。

     {

三、动态库生成示例

下一场编写翻译:

      retrun a+b;

1.单个文件生成动态库示例

我们编辑如下不难的多个程序文件:so_test.h、test_a.c和test.c,在test.c中要调用test_a.c中贯彻的函数test_a。
so_test.h文件内容:

#include <stdio.h>

void test_a();

test_a.c文件内容:

#include "so_test.h"

void test_a()
{
    printf("this is in test_a.../n");
}

test.c文件内容:

#include "so_test.h"

int main()
{
    test_a();

    return 0;
}

将此八个文件上传到Linux机器上,编写翻译生成动态库文件,之后调用库文件的上上下下经过如下所示:

~/zhouzhaoxiong/zzx/mylib/so> ll
-rw------- 1 zxin10 dba  95 Nov  4 17:37 so_test.h
-rw------- 1 zxin10 dba 109 Nov  4 17:37 test.c
-rw------- 1 zxin10 dba  84 Nov  4 10:57 test_a.c
~/zhouzhaoxiong/zzx/mylib/so> gcc test_a.c -fPIC -shared -o libtest.so
~/zhouzhaoxiong/zzx/mylib/so> ll
-rwxrwxrwx 1 zxin10 dba 8181 Nov  4 17:43 libtest.so
-rw------- 1 zxin10 dba   95 Nov  4 17:37 so_test.h
-rw------- 1 zxin10 dba  109 Nov  4 17:37 test.c
-rw------- 1 zxin10 dba   84 Nov  4 10:57 test_a.c
~/zhouzhaoxiong/zzx/mylib/so> gcc test.c -L. -ltest -o test
~/zhouzhaoxiong/zzx/mylib/so> ll
-rwxrwxrwx 1 zxin10 dba  8181 Nov  4 17:43 libtest.so
-rw------- 1 zxin10 dba    95 Nov  4 17:37 so_test.h
-rwxrwxrwx 1 zxin10 dba 11805 Nov  4 17:44 test
-rw------- 1 zxin10 dba   109 Nov  4 17:37 test.c
-rw------- 1 zxin10 dba    84 Nov  4 10:57 test_a.c
~/zhouzhaoxiong/zzx/mylib/so> ./test
this is in test_a...

介意,“./test”命令推行成功的前提是在情形变量中增加了.so文件所在的门径,那些门路能够在“.bash_profile”文件的“LD_LIBRARY_PATH”变量的值中加上。

大家可以看出,生成动态库文件的通令是“gcc test_a.c -fPIC -shared -o
libtest.so”,而将动态库文件编写翻译进代码的一声令下是“gcc test.c -L. -ltest -o
test”(-L.代表方今路径卡塔尔。

如此生成了动态库文件libtest.so之后,倘若还会有别的程序要调用test_a.c中落实的函数,只须要将so_test.h和libtest.so拷贝到对应的代码工程中,然后实行相似“gcc
test.c -L. -ltest -o
test”那样的吩咐就可以(前提是libtest.so所在的路径在遭遇变量中安装科学卡塔尔(قطر‎。

  1. g++ -fPIC -shared -o libtest.so test.c //生成动态链接库libtest.so
  2. g++ -o main main.c -ltest //调用动态链接库libtest.so
  3. ./main //运行main程序

  4. 链接时缺点和失误了有关指标文件(.o)

     }

2.多少个文件生成动态库示例

我们编辑如下轻巧的八个程序文件:so_test.h、test_a.c、test_b.c、test_c.c和test.c,在test.c中要调用test_a.c、test_b.c、test_c.c中落到实处的函数test_a、test_b、test_c。

so_test.h文件内容:

#include <stdio.h>

void test_a();
void test_b();
void test_c();

test_a.c文件内容:

#include "so_test.h"

void test_a()
{
    printf("this is in test_a.../n");
}

test_b.c文件内容:

#include "so_test.h"

void test_b()
{
    printf("this is in test_b.../n");
}

test_c.c文件内容:

#include "so_test.h"

void test_c()
{
    printf("this is in test_c.../n");
}

test.c文件内容:

#include "so_test.h"

int main()
{
    test_a();
    test_b();
    test_c();

    return 0;
}

将此七个公文上传到Linux机器上,编写翻译生成动态库文件,之后调用库文件的一体进度如下所示:

~/zhouzhaoxiong/zzx/mylib/test_so> ll
-rwxrwxrwx 1 zxin10 dba 8309 Nov  5 09:12 libtest
-rw------- 1 zxin10 dba   70 Nov  5 13:44 so_test.h
-rw------- 1 zxin10 dba  105 Nov  4 15:25 test.c
-rw------- 1 zxin10 dba   84 Nov  4 15:25 test_a.c
-rw------- 1 zxin10 dba   84 Nov  4 15:25 test_b.c
-rw------- 1 zxin10 dba   84 Nov  4 15:25 test_c.c
~/zhouzhaoxiong/zzx/mylib/test_so> gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
~/zhouzhaoxiong/zzx/mylib/test_so> gcc test.c -L. -ltest -o test
~/zhouzhaoxiong/zzx/mylib/test_so> ll
-rwxrwxrwx 1 zxin10 dba  8309 Nov  5 13:46 libtest.so
-rw------- 1 zxin10 dba    70 Nov  5 13:44 so_test.h
-rwxrwxrwx 1 zxin10 dba 11883 Nov  5 13:46 test
-rw------- 1 zxin10 dba   105 Nov  4 15:25 test.c
-rw------- 1 zxin10 dba    84 Nov  4 15:25 test_a.c
-rw------- 1 zxin10 dba    84 Nov  4 15:25 test_b.c
-rw------- 1 zxin10 dba    84 Nov  4 15:25 test_c.c
~/zhouzhaoxiong/zzx/mylib/test_so> ./test
this is in test_a...
this is in test_b...
this is in test_c...

留心,“./test”命令试行成功的前提仍是在遇到变量中增添了.so文件所在的路径,这么些门路能够在“.bash_profile”文件的“LD_LIBRARY_PATH”变量的值中丰盛。

咱俩得以看看,五个文件生成动态库文件的一声令下是“gcc test_a.c test_b.c
test_c.c -fPIC -shared -o
libtest.so”,而将动态库文件编写翻译进代码的通令是“gcc test.c -L. -ltest -o
test”(-L.代表前段时间路径卡塔尔国。

如此生成了动态库文件libtest.so之后,要是还会有其余程序要调用test_a.c、test_b.c、test_c.c中贯彻的函数,只需求将so_test.h和libtest.so拷贝到对应的代码工程中,然后实践相近“gcc
test.c -L. -ltest -o
test”这样的命令就能够(前提是libtest.so所在的路径在情状变量中设置科学卡塔尔。

代码与上述同样。

     int sub(int a,int b)

四、总结

有关生成静态库和动态库的命令,表达如下:

首先,在本文中,大家运用的退换静态库的下令形如“ar -r test.a
test.o”,个中,-r是replace的情致,表示要是当前布署的模块名曾在库中留存,则替换同名的模块。大家也足以用形如“ar
-cr test.a test.o”的授命来扭转静态库,当中-c是create的意趣,表示生成。

其次,在本文中,大家利用的变动动态库文件的指令形如“gcc test_a.c -fPIC
-shared -o
libtest.so”,此中,fPIC表示编写翻译为地点独立的代码,shared表示生成的库为分享库。将动态库文件编写翻译进代码的通令是“gcc
test.c -L. -ltest -o
test”,-L钦定库查找的职责(注意L前面还会有’.’卡塔尔国,表示在当前目录下搜索(要是在当前目录下的lib目录下搜寻,能够写成-L./lib卡塔尔(قطر‎;-l则钦定函数库名,在这之中的lib和.so省略(如这里的libtest.so就简写为testState of Qatar。

其三,使用ldd命令能够查看三个可执路程序信赖的分享库,该命令的施用示比如下所示:

~/zhouzhaoxiong/zzx/mylib/test_so> ldd test
        linux-vdso.so.1 =>  (0x00007fff1db6e000)
        libtest.so => /home/zhou/lib/libtest.so (0x00007fdbfff21000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fdbffb95000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fdc00124000)

可以见到,可施行文件test正视于三个分享库,此中libtest.so位于当下顾客的lib目录下。

编写翻译进度如下:

     {

参照他事他说加以调查资料:

1. 
2. 
3. 

  1. gcc -c test.c
  2. gcc -c main.c
  3. gcc -o main main.o

      retrun a-b;

那时,你会发掘,报错了:undefined reference to `test’.

     }

那就是最优越的 undefined reference
错误,因为在链接时开掘找不到某些函数的兑现公文,
本例中test.o文件中包含了test(State of Qatar函数的贯彻,所以只要按上边这种方法链接就没事了。

     int mul(int a,int b)

1. gcc -o main main.o test.o




【扩展】:其实上面为了让大家更加清楚底层原因,我把编译链接分开了,下面这样编译
也会报undefined reference错,其实底层原因与上面是一样的。

gcc -o main main.c //缺少test()的实现文件 

需要改成如下形式才能成功,将test()函数的实现文件一起编译。

gcc -o main main.c test.c //ok,没问题了

     {

  1. 链接时贫乏相关的库文件(.a/.so)

      retrun a*b;

在此,只举个静态库的例子,假设源码与上述一致。

1. 把test.c编译成静态库:
  gcc -c test.c
  sr -rc test.a test.o
  gcc -c main.c

2. 生成可执行程序:
  gcc -o main -main.o

    此时同样出现 undefined reference to `test'报错。其根本原因也是找不到test()函数的实现文
  件,由于该test()函数的实现在test.a这个静态库中的,故在链接的时候需要在其后加入test.a这个
  库,链接命令修改为如下形式即可。
  1. gcc -o main main.c ./test.a

     }

  1. 三个库文件链接顺序难题

     int div(int a,int b)

这种问题也非常的隐蔽,不仔细研究你可能会感到非常地莫名其妙。我们依然回到第3小节所讨论
的问题中,在最后,如果我们把链接的库的顺序换一下,看看会发生什么结果?

1. gcc -o main main.o func.a test.a

我们会得到如下的编译错误:

1. test.a(test.o): In function `test': 
2. test.c:(.text+0x13): undefined reference to `func' 
3. collect2: ld returned 1 exit status 
因此,我们需要注意,在链接命令中给出所依赖的库时,需要注意库之间的依赖顺序,依赖其他库
的库一定要放到被依赖库的前面,这样才能真正避免undefined reference的错误,完成编译链接。

     {

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章