OS39.【Linux】动态库和静态库 自制静态库

张开发
2026/4/20 10:07:33 15 分钟阅读

分享文章

OS39.【Linux】动态库和静态库 自制静态库
目录1.知识回顾提供方法的两种形式2.自制静态库并使用新建两个文件生成静态库文件的方法编写Makefile,生成静态库文件解释ar -rc命令的作用发布静态库使用静态库第一版代码第二版代码第三版代码3.总结4.补: gcc的搜索路径5.ar命令不仅仅可以打包静态库,还可以打包普通文件★★★6.gcc规定: 确保源文件和目标文件都出现在所有-l库选项之前1.知识回顾之前在OS12.【Linux】gcc和g以及动静态链接文章感性认识过动态链接和静态链接,这里贴上部分内容:动态链接: 程序运行时加载和链接库文件,换而言之,动态库是依附于可执行文件创建的进程来执行的,可执行文件自己不带动态链接库,需要到系统中调用动态链接库,完成后返回代码的调用处静态链接: 和动态链接不同,编译器使用静态库进行静态链接时,会将自己方法拷贝到目标程序中,程序在调用静态链接库的方法时不会转到系统中的动态链接库中执行其次,动态链接: 链接的是lib*.so文件 静态链接: 链接的是lib*.a文件提供方法的两种形式1.直接提供方法的源代码,开发者需要自行编译2.把方法的源代码想办法打包成库,向开发者提供库文件(实现) 头文件(声明)注意:不可以只给库文件!因为需要头文件来作库文件的说明书2.自制静态库并使用新建两个文件新建static_lib.c和static_lib.h,其中方法声明在h文件,方法实现在c文件static_lib.c:写一个简单的函数,方便演示double mydiv(double x,double y) { return x/y*1.0; }static_lib.h:#pragma once double mydiv(double x,double y);static_lib.h提供了mydiv方法的声明,static_lib.c提供了mydiv方法的实现生成静态库文件的方法回顾生成可执行文件的的过程:90.【C语言】编译和链接预处理(预编译)→编译→汇编→链接多个源文件生成多个目标文件,之后将多个目标文件链接起来,生成静态库文件这样做的:将所有o文件的集合(不含main函数的源文件生成的o文件)打包(ar命令)为静态库,然后让main.c使用,生成最后的可执行文件编写Makefile,生成静态库文件liblibdiv.a $(lib):static_lib.o ar -rc $ $^ static_lib.o:static_lib.c gcc -c $^ .PHONY:clean clean: rm -f :static_lib.o libdiv.a看看依赖关系: static_lib.c→static_lib.o→libdiv.a,而libdiv.a就是静态库文件注意: 静态库的名称必须按照lib*.a格式来,否则gcc报错解释ar -rc命令的作用ar是gnu归档(archives)工具,rc表示(replace and create)r选项: 在归档文件中替换已存在的成员如果指定的文件在归档中不存在,其会被添加到归档的末尾c选项: 如果归档文件不存在,则创建归档文件,否则ar命令会报警告发布静态库将库文件放到一个目录中,头文件放到另外一个目录中,为Makefile添加以下内容:.PHONY:release release: mkdir -p lib/include mkdir -p lib/libdiv mv libdiv.a lib/libdiv/libdiv.a mv static_lib.h lib/include/static_lib.h使用静态库编写main.c第一版代码#include stdio.h #include static_lib.h int main() { printf(1 / 2 %lf\n,mydiv(1,2)); }报错是因为找不到头文件gcc是这样寻找头文件的:对于像#include 这样包含的头文件,如果不带路径,只有头文件的名称,那么gcc首先在自己当前所处的目录中找 2.如果第第一步找不到,那么就在系统的头文件的存储目录中找头文件的存储目录存放在环境变量中,一般是/usr/include第二版代码为了方便起见,不将static_lib.h所处的理解添加到gcc搜索头文件的路径上,直接在源文件中指定可以使用绝对路径:#include stdio.h #include /home/guest/lib/include/static_lib.h int main() { printf(1 / 2 %lf\n,mydiv(1,2)); }也可以使用相对路径:#include stdio.h #include lib/include/static_lib.h int main() { printf(1 / 2 %lf\n,mydiv(1,2)); }但无论哪种写法都会报链接错误:虽然能找到头文件,头文件承诺有mydiv函数,但是gcc找不到mydiv方法实现(即libdiv.a静态库文件),一般gcc默认在/lib64中找库文件,那么应该为gcc指定库的路径查阅gcc官方手册Using the GNU Compiler Collection For gcc version 15.2.0知道:摘录部分内容3.16 Options for Linking-llibrary-l librarySearch the library named library when linking. (The second alternative withthe library as a separate argument is only for POSIX compliance and is notrecommended.)The -l option is passed directly to the linker by GCC. Refer to your linkerdocumentation for exact details. The general description below applies to theGNU linker.The linker searches a standard list of directories for the library. The directoriessearched include several standard system directories plus any that you specifywith -L.Static libraries are archives of object files, and have file names likeliblibrary.a. Some targets also support shared libraries, which typicallyhave names like liblibrary.so. If both static and shared libraries arefound, the linker gives preference to linking with the shared library unless the-static option is used.It makes a difference where in the command you write this option; the linkersearches and processes libraries and object files in the order they are specified.Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file foo.o but before bar.o.If bar.o refers to functions in ‘z’, those functions may not be loaded.这里使用-L选项:gcc main.c -L ./lib/libdiv/但仍然报链接错误:原因:-L只是告诉了库文件的路径,一个目录里面可能有很多库文件,但gcc不知道链接哪个库文件,因为上方的命令中没有指定库名可以使用-l来指定库:#源文件已经指定头文件的路径 gcc main.c -L ./lib/libdiv/ -ldiv注:库的完整名称是libdiv.a,只需要-ldiv(-l(小写的)后紧跟着库名,库名是去掉前缀和后缀剩下的部分,不需要写前面的lib和后面的.a)运行结果:第三版代码如果这样写:#include stdio.h #include static_lib.h int main() { printf(1 / 2 %lf\n,mydiv(1,2)); }那么gcc需要知道static_lib.h的路径,可以使用-I(大写的i)来指定头文件的搜索路径:gcc main.c -L ./lib/libdiv/ -ldiv -I ./lib/include3.总结用ldd查看a.out的动态库:ldd的作用是显示可执行文件的动态库,自制的是静态库,因此ldd是不会显示的,上图从侧面来看:一个可执行程序的动态库和静态库是可以共存的注意gcc使用静态库编译的细节:由于gcc默认采用动态链接的方式(言外之意:如果系统中只提供静态链接库而且用户也没有提供动态链接库,那么gcc 则只能对该库进行静态链接),那么想让gcc编译自制的静态库是需要添加一些选项的细节一: 给头文件路径: gcc的-I(大写的)选项或者直接在源代码里面指定相对路径或决定路径)细节二: 给库文件路径: gcc的-L细节三: 给库文件名: gcc的-l (小写的,即link)如果系统中需要链接多个库,则gcc可以链接多个库结论: 链接第3方库必须告诉gcc的头文件路径和库文件路径和库文件名称如果不想写那么多选项,可以这样做:头文件拷贝到系统的include目录,库文件拷贝到系统的lib目录或者:将软链接放到gcc的搜索路径下★其实不建议将自制的库文件放到系统的默认搜索路径下,容易和系统的库文件冲突4.补: gcc的搜索路径gnu gcc官网给出了搜索路径: https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html其指出: 默认的搜索路径和特定的系统、gcc的配置和安装路径有关,可以通过-v选项来查看搜索路径,例如:gcc -v /dev/null -o /dev/null5.ar命令不仅仅可以打包静态库,还可以打包普通文件具体讲解参见OS39.5.【Linux】分析ar命令生成的归档文件的格式文章★★★6.gcc规定: 确保源文件和目标文件都出现在所有-l库选项之前文章上方的编译命令写法没问题:gcc main.c -L ./lib/libdiv/ -ldiv -I ./lib/include但不能将-ldiv提到main.c前面,即不能写成gcc -ldiv -L ./lib/libdiv/ -I ./lib/include main.c,gcc会报链接错误这不是gcc的bug,而gcc规定确保源文件出现在所有-l库选项之前之前有开发者写邮件反馈过这个问题:https://gcc.gnu.org/pipermail/gcc-bugs/1999-December/031804.htmlhttps://gcc.gnu.org/pipermail/gcc-bugs/1999-December/031096.htmlhttps://gcc.gnu.org/bugzilla/show_bug.cgi?id54708gcc贡献者之一的Martin v. Loewis(Martin v. Loewis commit)在给Bug with library link邮件回复中是这样说的:翻译过来:1.链接器会从库中搜索缺失的符号2.目标文件和库按照从左到右的顺序被处理,当没有符号缺失时,该库就不会被使用C/C代码编译后会产生“目标文件”,目标文件里有些函数/变量只有声明没有定义,那么就称为缺失的符号gcc main.c -L ./lib/libdiv/ -ldiv -I ./lib/include是正确的写法,链接器的具体操作步骤为: 读到main.c记录main.c中缺失的符号,读到-ldiv,对照库去补main.c中缺失的符号

更多文章