从制作者和使用者两个角度来谈
1.库的使用
假设老师安排课后作业让模拟实现C语言标准库中的文件操作,并进行接口调用测试
张三无能为力,就找到了学霸🎓李四,李四没有把.c文件给张三,因为如果给了,老师追查下来谁抄的谁说不清楚,另一方面,就算拿到源码,张三也要编译成.o文件,所以直接给了张三mystdio.o也就是文件操作实现代码编译后的文件,给.h文件是告诉张三接口是什么,也就是使用手册,接着张三在main.c内调用文件接口实现文件操作,main.c编译生成.o文件,接着把.o文件链接到一起,形成可执行程序,即可运行
而其实我们都是张三,而李四就是库的制作者,在使用C语言标准库函数也是一个道理,我们用到的库本质是.o文件的集合,而官方只需要提供头文件和.o文件打包之后的.a(静态库)和.so(动态库)即可
我们不是李四,一方面很多代码没必要从零写起,另一方面,大佬实现的也确实好,统一接口有利于跨平台移植和兼容性
下面将main.c编译形成main.o,接着main.o和mystd.o链接形成mytest可执行程序,./test执行,发现多了log.txt这个文件,也就是执行成功,
使用ldd 可执行程序名可以查看可执行程序依赖的动态库 list dynamic dependencies,libc.so.6是C语言标准库的软链接,libxxx.so,lib是前缀,so是后缀(动态库),xxx是库名

ld是加载器
/lib64是linux系统查找库的默认路径,查可执行程序的默认路径是/usr/bin,/lib64/libc-2.17.so是C标准库



g++,gcc默认是动态链接,如果要静态链接的话,需要加-static选项,可以看到静态链接比动态链接得到的可执行程序大很多

可以看到/lib64其实是/usr/lib64的软链接

而且可以看到我们装了c语言静态库和动态库以及C++的动态库,因此编译C语言可以静态链接/动态链接

为什么静态链接比动态链接体积大,以printf为例,静态链接是把printf的实现在链接的时候从stdio.o把printf的实现拷贝到test.o,而动态链接是在test里留一张符号表,告诉系统我要用函数printf,在哪个库里,运行的时候,加载器找到lib64.so.6,把printf的地址填到符号表,接着运行时跳转执行
-c就是只编译xxx.c文件,如果没有-o就是生成xxx.o,如果有-o name.o 名称就是name.o,gcc xxx.o -o xxx就是链接,gcc xxx.c -o xxx就是编译+链接

推荐做法是第一种,下面也说明只有链接的时候各个.o文件才会产生关联

即使test.c是空的,也可以编译

make发现target需要依赖main.o,mystring.o,mystring.o,没有哪个生成哪个,下面.c进行编译


库文件本质是.o文件的集合,想办法打包成.a或.so文件
2.库的制作
2.1 静态库制作
使用ar命令,rc是replace和create,没有就创建,有就用新建的代替原有的libmystdio.a,lib是library库的缩写,.a是静态库的后缀,libxxx.a,xxx是库的名称

那么只需要提供库(实现,打包好的.o文件,还可以保护源码)和头文件(声明,怎么用)就可以


而gcc是专门编译C语言的,所以默认知道C语言的标准库在/lib64(是/usr/lib64的软链接)



把头文件和.a文件拷贝到系统路径下,但是gcc main.c还是找不到实现,因为是第三方库,只有指明用到的库才可以

把系统路径下的mystdio.h mystring,h libmystdio.a都删掉后,发现编译main.c的时候找不到头文件,但是把头文件拷贝到当前路径下就可以找到头文件,但是把libmystdio.a拷贝过来还是显示找不到,所以可以得出,头文件可以在当前路径+系统路径,但是库文件只会在系统路径

也可以-I告诉编译器头文件在哪找

无论是头文件还是库文件默认查找系统路径、当前路径
所以使用第三方库,要么拷贝到系统路径下(或者在系统路径下创建第三方库的软链接),并且指明库名;
要么-I(大i)指明头文件路径,-l指明库名,-L指明库路径

使用ar -tv 静态库名,可以查静态库由哪些.o打包而成,t是table,v是verbose



2.2 动态库制作
gcc默认形成可执行程序,加-shared形成动态库

-fPIC,PIC,position-indendpent code位置无关代码,因为动态库可能被多个程序共享,多个程序的虚拟地址空间可能不一样,因此编译时根本不知道库会加载到哪块内存
头文件会在当前路径和系统路径下查找


3.其它问题
3.1运行时找动态库
库文件不在当前路径和系统路径,只在./mylib/lib
因为编译的时候只是把库文件的地址告诉编译器,而运行程序,也就是进程不知道库在哪,系统和当前路径找不到

拷贝动态库到系统路径下

软链接

改变环境变量

但是静态链接是没有这个问题的,因为静态链接直接把方法复制到了程序中,而动态链接只是记住动态库名称,运行的时候加载器去找(当前路径/系统路径),而在编译时候告诉动态库路径是告诉编译器的,和进程无关,解决方法是,把第三方动态库拷贝/创建软链接到系统路径下,或者更改环境变量
而动态链接的命令是gcc -shared,静态是ar -rc,也说明动态库比静态库更常用一些
但环境变量关机就没了,当然也可以改bash下的profile等配置文件进行环境变量的配置

还可以更改/etc/ld.so.conf.d,在目录下创建diy.conf,填上libstdio.so的路径

ldconfig让/etc/ld.so.conf.d下的.conf文件全部生效

如果静动态库同时存在,优先选哪一个?如果只有一个?
理解一下C语言的FILE对象是C标准库申请的,怎么理解,结合模拟实现来理解一下,myfopen打开文件成功的情况下,在函数内部动态申请一个myFILE对象,并把指针返回
库是实现,最终操作还是由用户来做的,比如下面类似fopen(库函数)申请FILE结构体对象,代码是C标准库实现的,但是用户调用的方法,所以还是用户申请的(空间等)

3.2 动静态库同时存在

如果动静态库同时存在,优先使用动态库,包括很多时候提供的都是动态库,静态库几乎很少甚至需要自己安装

从下图可以看到,静态链接会把main.c使用的方法都拷贝到main.o,只要是出现过的,无论是if还是else用到的,以.o为最小拷贝单位,比如只用到mystdio.o中的myfopen,也要把mystdio.o都拷贝到main.o

可以看到-static一定需要静态库,有动态库不能用

3.3 演示使用第三方库
具体使用什么库还是和具体应用场景有关,qt是C/C++写的图形化界面库,是库就会提供接口,使用qt图形化界面显示本质就是学习qt库函数,安卓也是库,负责的库比如qt,简单的比如图形界面easyx,老一点的比如mfc,学库都是在学库方法,必要的时候有一些自己编程的技巧
ncurses在linux终端可以显示图案
test.c的代码,用来画心形
使用的时候要指明库名,因为gcc只认c语言的库,其它库不认识

安装的本质就是把头文件和库文件分别拷贝到系统路径下

使用第三方库也可以使用AI帮助筛选常用的代码结构和接口,进行熟悉
ldd是查看运行时库,下图可以看到查到的路径和编译时给的路径不一样,因为编译只确定有没有,动态链接的时候发现方法实现确实是在给到路径下的动态库中有实现,编译通过,但是运行的时候,还要找库(系统路径库文件或其软链接,或者/etc/ld.so.conf.d目录下.conf文件中的路径,或者环境变量LD_LIBRARY_PATH表明的路径),在/etc/ld.so.conf.d/diy.conf文件下写入的是/home/guchen/linux-code-replication/test_5_19/test/mylib/lib,在这个路径下找到了mystdio的动态库

od 可执行程序名 可以查看可执行程序的八进制转储
