网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
为什么要有库
本质:提高开发效率(C++中的STL库就是典型的例子)但是要明白,在学习的过程中,我们不能知识肤浅以为会使用库里面的一些接口就是学会了如何使用库,以为自己拥有了使用库来提高开发效率的能力,因为单单只是认识库里的接口,然后使用库,这样的学习方式是不好的,不能将知识融会在一起,所以最好的学习方式,就是自己造轮子(自己去了解库中的接口是如何实现的),只有这样,我们才可以熟练的使用轮子,从而真正做到提高开发效率!
自己封装库的使用方式
类似于我们之前博客中有关文件操作库的实现,本质上就是在调用系统调用接口的同时,自己封装实现一些我们以后开发可以用到的接口,类似C++库和C库,里面有非常多我们在编写代码时可能会用到的函数接口实现,例如:sort、cin、cout、printf、scanf
等!当然最关键的还是C++中的STL库,STL是属于语言库中的功能库,各种功能十分强大
并且要注意,不同的系统里面的库是不同的,使用的库也是不同的,所以为什么有的软件在安卓系统上可以使用,但是在i苹果系统(ios)上却不能使用,原因就是因为系统不同,库的实现不同,使用的库不同
如下图:就是Linux系统上自带的库文件
所以如上图,可以看出,在一个系统中,库是非常多的,并且发现,一个库文件的名称,就是libxxx.so或者libxxx.a
,所以将库文件开头的lib
去掉,此时我们就得到了这个库的名称,并且 注意
:云服务器上一般只会存在动态库,而不会存在静态库,因为静态库需要单独安装
动态库和静态库的区别
- 静态库
这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大–空间,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
- 动态库
这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用–时间,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级/更新比较方便
总结:静态库需要将整个库整合到目标代码,而动态库可以通过条件编译指令的方式,通过头文件的展开,将函数接口声明,然后在目标代码中执行到特定的函数接口时,直接去调用库中的相应函数接口即可(因为有头文件的展开声明)
库文件的具体使用方式:
所以在自己使用库的过程之中,其实本质上,就是将别的函数接口包装在一个文件之中,然后为了避免该源码文件的泄露,将该文件生成一个二进制文件,然后当别的文件需要使用这个接口的时候,将这个接口的二进制文件复制到该目标文件所在的路径中,然后将要执行的文件也进行运行,然后将需要使用到的库文件和目标文件加载到内存,最后就可以通过好几个不同的可重定向目标文件生成出一个可执行程序,如下图:
所以我们可以发现,我们可以通过自己封装一个库文件,然后通过相应的头文件来调用这个库文件,并且将这些文件加载到内存生成可执行文件,最终通过头文件的形式给普通文件使用(例:main.c
)
并且注意:我们不仅可以如上图一般,将自己的库文件给当前路径的文件使用,也可以给其它路径下的文件使用,也就是可以把这个库文件给别人使用,相当于语言库和功能库一样,让全世界的人都可以使用,并且为了不把自己的源代码暴露出去,我们也可以将相应的库文件生成一个可重定位的目标二进制文件,然后再通过头文件的方式,让别人去调用,如下图所示:
如上图,可以看出,当别人需要使用我们实现的库文件时,我们是可以不需要把源文件给它的,可以把库文件生成二进制文件给他,这样他依然可以根据头文件中的接口声明,去调用到二进制库文件中的实现
静态库的封装和使用
如上述所说,可以发现,如果是自己想要使用库文件,或者是别人想要使用我们的库文件,都需要通过当前路径的库文件或者库文件的二进制文件实现,所以使用库文件的前提就是要拥有库文件,然后才是将库文件和目标文件一起进行编译,最后生成可执行程序,所以如果在函数接口非常多的情况下,也就是需要调用的库文件非常多的情况下,这种方式不适用的,因为首先需要去拷贝许多的库文件或者库文件的二进制文件到当前路径之下,然后才可以进行目标文件的编译,否则就不允许你直接使用头文件去调用已经实现了的函数接口,所以这也就是库的由来,为了减少库文件的拷贝,我们可以将这些许多的二进制库文件封装成一个库, 然后以后需要用到的时候,直接将这个库替换到目标文件中就行,不需要一个一个库文件的拷贝,而是很多很多库文件的拷贝,这样就可以大大降低调用库文件的成本,所以具体步骤如下:
静态库打包库文件指令:ar -rc libmymath.a
,然后具体步骤如下图:
可以发现,当我们想要使用我们自己实现的库时,我们不仅需要有这个库,更重要的是要让编译器可以找到这个库,指令:gcc -o mytest main.c -L. -l mymath
所以像平时我们下载库或者是软件,本质上,这些库和软件的路径都是系统自己规定好的,在使用的时候,系统默认可以直接找到(环境变量知识)
使用第三方库的方式
如上图,所以我们平时为了给别人使用库,我们一般是先将库文件和头文件放到include
和lib
文件中,然后将这两个文件压缩成一个压缩包文件,这样就可以让别人更方便的拿到对应的库文件和头文件,所以当其他用户拿到了对应的库文件和头文件时,此时就可以使用它们进行自己目标文件的代码编写了,但是此时由于库文件和头文件都不在gcc或者g++的默认寻找路径中,所以想要直接使用的话,此时就需要像上述指明库的路径一样,将头文件的路径也指明,指令:gcc -o mytest main.c -I ./include -L ./lib -l mymath
具体如下图所示:
第三方库的使用原则:
- 拥有特定的头文件和库文件
- 如果没有默认安装到系统gcc或者g++默认的搜索路径下,用户必须指明对应的选项,告知编译器头文件的路径和库文件的路径
- 所以对于任何软件,安装和卸载的本质就是将对应软件的库文件和头文件拷贝到系统特定的路径下 (文件系统知识)
当然,你也可以直接自己把头文件和库文件拷贝到系统指定的头文件和库文件路径之下,此时就不需要再指定头文件路径了,系统编译器此时在默认路径下就可以找到,但是由于我们的库不是系统库,还是一个第三方库,所以任然需要指明链接的库的名字,所以将我们下载的库和头文件,拷贝到系统默认路径下,就是等于在Linux系统下安装库
第一方库:语言库
第二方库:操作系统的系统接口
第三方库:我们自己实现的库(即便是已经全部安装到了系统路径中,编译器在寻找的时候,还是需要指明链接的库名称)
由于任何软件,安装和卸载的本质就是将对应软件的库文件和头文件拷贝到系统特定的路径下,所以在下载和卸载软件的时候,就要访问系统特定的路径,所以无论是安装软件还是卸载软件,都是需要提升权限或者使用超级用户
动态库的封装和使用
大致上和静态库的封装使用相同,但是又有一些本质上的不同,如下:
动态库打包库文件指令:gcc -shared -o libmymath.so *.o
,发现,编译器自带打包动态库的能力,所以充分说明,在Linux系统中,编译器大部分使用的都是动态库,具体如下图所以:
第三方库使用方式
如下图,第三方库的使用方式和静态库是相同的,这里不多做讲解
当我们生成了可执行程序之后,此时如果我们想要运行这个可执行程序,会发现一个问题,如下图:
报错的主要原因是: 我们以为我们已经将库的路径和头文件的路径告诉的操作系统,但是本质上却没有,而只是将头文件和库文件的路径告诉了gcc编译器,所以对于gcc编译器来说,此时它可以生成我们想要的mytest可执行程序,但是对于操作系统来说,此时它就不能让可执行程序变成运行程序,因为我们没有将库文件路径告诉操作系统,所以报错!
动态库不能被操作系统链接解决方法
所以明白了上述问题,此时我们就来解决这个问题吧!但是前提是搞明白一个问题,为什么静态库不会出现操作系统不能链接问题,而动态库会出现操作系统不能链接问题,如下:
原因就是:静态库,链接原则是将用户使用的二进制代码直接拷贝到目标可执行程序中!但是动态库不会,因为动态库并不会直接将二进制代码直接拷贝到可执行程序中,而是让目标程序通过头文件的方式去调用相关库中的函数接口实现
具体解决方法
1.将库路径导入到环境变量中(临时)
2.软连接(永久)
3.配置文件方案
查看一个可执行程序依赖的第三方库指令:ldd 可执行程序
,具体如下图所示:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!