静态库与动态库的那些事

2020-08-10 10:41:49 浏览数 (1)

【根据近期理解,进行整理,如果错误,欢迎指正】

编译流程:

分为四个步骤:预处理 编译 汇编 链接

(1)预处理(Preprocessing)

实现功能:

完成宏替换、文件引入、以及去除空行、注释等,为下一步的编译做准备。也就是对各种预处理命令进行处理,包括文件的包含、宏定义的扩展、条件编译的选择等。

命令:

代码语言:javascript复制
$ gcc -E test.c -o test.i      #test.c为源文件

选项-E让gcc在预处理结束后停止编译,“test.i”文件为预处理后输出文件。-o 指定输出文件

预处理后文件会变大很多。

(2) 编译(Compilation)

实现功能:

将预处理后的代码变成汇编代码。在这个阶段中,首先要检查代码的规范性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,再把代码翻译成汇编语言。 2.编译程序执行时,先分析,后综合。分析,就是指词法分析、语法分析、语义分析和中间代码生成。综合,就是指代码优化和代码生成。 3.大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,也有的是先产生汇编语言一级的符号代码文件,再调用汇编程序进行翻译和加工处理,最后产生可执行的机器语言目标文件。

命令:

代码语言:javascript复制
$ gcc -S test.i -o test.s

选项-S让gcc在编译结束后停止编译过程,“test.s”文件为编译后生成的汇编代码。

(3)汇编(Assemble)

实现功能:

汇编就是把编译阶段生成的“.s”文件转成二进制目标代码,也就是机器代码(01序列)。

命令:

代码语言:javascript复制
$ gcc -c test.s -o test.o

选项-c让gcc在汇编结束后停止编译过程,“test.o”文件为汇编后生成的机器码目标文件。

(4)链接(Linking)

实现功能:

链接就是将多个目标文件以及所需的库文件链接生成可执行目标文件的过程。

命令:

代码语言:javascript复制
$ gcc test.o -o test

-o本质上是一个重命名选项。不适用-o选项时,默认生成的是a.out文件。这里生成的是可执行文件test。

以上是编译实现的四个步骤过程。

运行

执行可执行程序

代码语言:javascript复制
$ ./test

库是一组目标文件的包,就是一些最常用的代码编译成目标文件后打包存放。

静态库

静态库(.a 、.lib)

1.静态库是在汇编过程生成的,加载静态态库是在链接过程之前

2.静态库实际就是一些目标文件(一般以.o结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段。 3.在链接步骤中,链接器将从库文件取得所需代码,复制到生成的可执行文件中。这种库成为静态库。

可执行文件中包含了库代码的一份完整拷贝,在编译过程中被载入程序中。

缺点就是多次使用就会有多份冗余拷贝,并且对程序的更新、部署和发布会带来麻烦,如果静态库有更新,那么所有使用它的程序都需要重新编译、发布。

生成静态库的过程

1. 将源文件生成 目标文件(完成到汇编的过程)

代码语言:javascript复制
$ gcc -c test.c -o test.o

2. 使用ar命令将test.o打包成libtest.a静态库

代码语言:javascript复制
$ ar rcs libtest.a test.o

3. 查看静态库的具体内容, 静态库其实就是目标文件的集合

代码语言:javascript复制
$ ar t libtest.a 
test.o

动态库

动态库(.so 、.dll )

1.动态库是在汇编过程生成的,加载动态库是在程序运行过程中

2.动态库在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用。 3.系统只需要载入一次动态库,不同的程序可以得到内存中相同动态库的副本,因此节省了很多内存。

4.程序运行可执行文件加载动态库,需要动态的设置动态库地址,才能运行。即将动态库放置在可执行文件同级目录下

这里和静态库有着很大的不同之处

生成动态库的过程:

1. 将源文件生成 目标文件(完成到汇编的过程)

代码语言:javascript复制
$ gcc -c test.c -o test.o

使用-share和-fPIC参数生成动态库。

代码语言:javascript复制
$ gcc -shared -fPIC -o libtest.so test.o

根据前面对动态库,静态库的理解,下面进一步理解动态链接库,静态链接库

根据上面的理解,以为lib为静态库,dll为动态库,这理解并不错,但不全面。

lib分两种

(1) lib包含所有内容的静态库,应用程序用它即可,这是我上面理解的一种

(2) lib只包含头部信息,为了配合dll使用,dll包含函数内容,此时,可以观察出lib比dll小很多:

比如:opencv中的这两个文件。lib 只是一些函数入口,dll包含函数内容。

因此可以总结为,共有两种库:

动态链接库dynamic link library

一种是LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library。

静态链接库static link library

一种是LIB包含函数代码本身,在编译时直接将代码加入程序当中,称为静态链接库static link library。

静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

共有两种链接方式:

动态链接使用动态链接库,允许可执行模块(.dll文件或.exe文件)仅包含在运行时定位DLL函数的可执行代码所需的信息。

静态链接使用静态链接库,链接器从静态链接库LIB获取所有被引用函数,并将库同代码一起放到可执行文件中。

关于lib和dll的区别如下:

(1)lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。

(2) 如果有dll文件,那么lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容(动态链接库);

如果只有lib文件,那么这个lib文件 是静态编译出来的,索引和实现都在其中(静态链接库)。使用静态编译的lib文件,在运行程序时不需要再挂动态库,缺点是导致应用程序比较大,而且失去了动态库的灵活 性,发布新版本时要发布新的应用程序才行。

(3)动态链接的情况下,有两个文件:一个是LIB文件,一个是DLL文件。LIB包含被DLL导出的 函数名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是 DLL中相应函数代码的地址,从而节省了内存资源。DLL和LIB文件必须随应用程序一起发行,否则应用程序会产生错误。

使用lib需注意两个文件:

(1).h头文件,包含lib中说明输出的类或符号原型或数据结构。应用程序调用lib时,需要将该文件包含入应用程序的源文件中。

(2).LIB文件,目标文件的集合。

使用dll需注意三个文件:

(1).h头文件,包含dll中说明输出的类或符号原型或数据结构的.h文件。应用程序调用dll时,需要将该文件包含入应用程序的源文件中。

(2).LIB文件,是dll在编译、链接成功之后生成的文件,作用是当其他应用程序调用dll时,需要将该文件引入应用程序,否则产生错误。

(3).dll文件,真正的可执行文件,开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,并不需要.lib文件和.h头文件。

参考链接:

https://www.cnblogs.com/405845829qq/p/4108450.html

https://blog.csdn.net/roy_xing/article/details/90372477?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

0 人点赞