ppt免费下载雷锋网站,WordPress5分钟建站,wordpress faq模板,服务器安全证书验证失败一、前言
为什么要使用别人的代码#xff1f; 主要是为了提高程序开发的效率和程序的健壮性。 当别人把功能都实现了#xff0c;然后我们再基于别人的代码去做二次开发#xff0c;那么效率当然就提高了。其次#xff0c;这里基于的别人当然不是随便找的一个人#xff0c;…一、前言
为什么要使用别人的代码 主要是为了提高程序开发的效率和程序的健壮性。 当别人把功能都实现了然后我们再基于别人的代码去做二次开发那么效率当然就提高了。其次这里基于的别人当然不是随便找的一个人而特指的是顶尖的工程师也就是说如果我们的代码出了问题一般不会去怀疑是别人的库的问题这也就增加了代码的健壮性。 换而言之如果我写我的代码别人写别人的代码那么在机制上我们的工作就得以分解假如我是一个写调用、逻辑特别严谨的程序员假设别人是写库写得很好的一个人我们两个都有比较完善的工作方式那么两个人合起来就可以提高代码的效率和健壮性而不需要两方面都兼顾。 程序的健壮性更直观的理解是我们在百度搜索时输入乱码而百度却不会崩溃。 动态库libc.solibc.so静态库libc.alibc.a 一般在命名上去掉前缀 lib去掉 . 和之后的内容剩下的就是库名所以这里就是 C 库和 C 库。 C/C 体系中如何使用别人的功能
生成可执行的方式有静态链接和动态链接对应的是静态库和动态库。 比如张三是一名大一新生他在写作业时突然有了一个上网的需求但因为周边环境不熟悉所以找了学长询问附近网吧的地点随后就跑去玩了几个小时然后回来后接着继续写作业这就叫作动态链接。两年后张三已经是一名大三师兄了要开始准备找工作了家里给他买了一台电脑当他正在学习时想要上网就不用再特意跑去网吧了而只需要打开自己的电脑就行也就是说静态链接并没有和外界产生关联。 一般我们写的程序中大部分都是动态链接因为动态链接中不需要把库中的内容进行过多的拷贝所以相对而言它的编译效率较高这是其一我们有时候下载一些软件比如 VS2019它上面会有一些组件比如 C、C# 等但这些组件并没有在我们的硬盘中而当我们要安装时它会帮我们找到这些组件下载这样有个好处就是我们需要什么就直接下载什么而不是直接一堆东西直接装在机器上而自己需要使用的组件却寥寥无几所以这里就采用动态库的方式实现这是其二。
当然静态链接也有属于自己的使用场景一般在服务器上大部分也都是动态链接不过有时候需要将服务在很多机器上部署那么单纯的动态链接就可能会出问题因为动态链接是在程序运行之后才去对应加载到内存中万一有个库丢失了那么程序就挂了。所以有时候一些程序它也会采用静态链接好处就是它不依赖于任何动态库坏处就是效率比较低。比如静态链接的大小是动态链接的一百倍要把静态链接这个程序下载下来就只能全部下下来而动态链接则是边下边用。总的来说两者各有利弊没有绝对的好坏之分。 动态链接的生成的可执行程序的体积往往比较小节省资源磁盘、内存但是它依赖第三方库有一定的风险一旦库丢失可执行程序不可执行网吧被查封了。静态链接虽然生成的可执行程序的体积较大浪费资源磁盘、内存但是它不依赖第三方库一旦库丢失可执行程序依旧可以执行网吧被查封了照样可以玩。 静态库.a程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。动态库.so程序在运行的时候才去链接动态库的代码多个程序共享使用库的代码。 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表而不是外部函数所在目标文件的整个机器码。
在可执行文件开始运行以前外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中这个过程称为动态链接dynamic linking。
动态库可以在多个程序间共享所以动态链接使得可执行文件更小节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用节省了内存和磁盘空间。 二、静态库的制作打包与使用不使用 makefile
1、准备工作 别人要使用这些接口最直观的就是直接给源文件别人就可以直接在当前目录创建文件然后直接包含它们就行。但是如果不想让别人看到我们的源文件是如何实现的而只是告诉别人它们的作用那么就需要使用库。其实动静态库的属性就是不想暴露自已的源代码所以可以直接打包给别人。
我们让别人来使用我们的库前提是别人需要知道我们的库能给他们提供什么方法这是通过头文件来体现的头文件可以暴露。但光有头文件还不行里面只有声明还需要有实现所以就将方法所在的源文件进行编译然后打包到库。 2、生成静态库
1gcc -c mymath.c -o mymath.o / gcc -c myprint.c -o myprint.o 先将源文件汇编后生成 .o 文件它虽然也是二进制文件但是它还不可以执行因为还差最后一步链接。所以库就是在编译过程中在链接的前一步停下来编译成可被链接的目标文件。 如果只把 .h 和 .o 给别人别人可以用吗 可以。 2ar -rc libhello.a mymath.o myprint.o ar Archive 是 gnu 归档工具 rc 表示 replace and create意思就是如果要生成的库中已经包含了对应的 .o 文件就 replace否则就 create 。 此时 libhello.a 就是静态库所以我们将来只需要把 mymath.hmyprint.hlibhello.a 打包交付给其它人即可而 libhello.a 就由 mymath.hmyprint.h 来说明。这也就是为什么大部分库在提供的时候一般是提供库文件 头文件也就是为什么在使用 C 语言的时候永远都是 #include stdio.h然后在写 printf 的时候直接调用最后链接库本质上就是因为系统在装的时候就把库文件和头文件给装了而 C 语言的源代码就不需要在系统中了。就相当于把源文件先编译一大部分不要链接再把所以编译好的 .o 文件打个包让别人去用这就是静态库。 提示libmymath.a 前缀必须是 lib。 3查看静态库中的目录列表 t列出静态库中的文件 vverbose 详细信息 3、使用静态库 李四想用张三写好的 mymathmyprint 代码但是张三不想让李四看到自己是怎么实现的所以就将它们打包好变成库 hello复制给李四李四人拿到后就可以通过头文件知道了这个库是干嘛的 需要提前将库拷贝到系统的默认路径下这个过程叫作库的安装。 所以李四就在 main.c 中使用张三写好的库随后 gcc main.c -o main报错说 myprint.h 是没有这个文件或目录的原因是 myprint.h 既没有在当前路径下也没有在默认路径下这里的当前路径是指要和 main.c 在同一级路径下。所以这里 gcc 还要再加一个选项 -I后面跟上 ./hello意思就是 gcc 在编译程序找头文件的话除了在当前路径下系统路径下你也要在 ./dir 下去找。 此时还有报错但对比上一次报错显然头文件已经找到了。此时报的是在链接时找不到 addToTarget 方法所以还 gcc 还要在加上一个选项 -l后面跟上 ./hello表明库文件在这个目录下。只不过这里是巧合头文件和库文件都在 ./hello下。 然后又报错了原因是指明了库路径但并没有指明要访问的是这个路径下的哪一个库。如果这个路径下有多个库就需要指明了。所以还要再加一个选项 -l后面跟上 libhello.a 去掉前后缀也就是 hello。此时就完成了链接形成了可执行程序 1-I 路径 告诉 gcc 除了默认路径以及当前路径在指定路径下也找一下头文件。而一般 linux 下头文件的默认路径在 /usr/include 下其中我们就看到了最熟悉的 C 文件 stdio.h。 2-L 路径 告诉 gcc 除了默认路径以及当前路径之外在指定的路径下也找一下库文件。而一般 linux 下库文件的默认路径在 /lib64 或 /usr/lib64 下 其中你往下翻也可以找到比较熟悉的 C 语言的静态库和动态库libc.a 和 libc.so。 这里需要说一下实际系统在搜索头和库的时候除了这些默认路径还有其它的路径。另外不同 linux 发行版甚至同一发行版在路径上都可能不太一样所以具体问题具体对待。 3-l 库名 需要进一步具体说明要链接哪一个库。 4、库搜索路径 从左到右搜索 -L 指定的目录。 由环境变量指定的目录LIBRARY_PATH 由系统指定的目录 /usr/lib /usr/local/lib 为什么 C/C 在编译的时候从来没有明显的使用过 -I/L/l 等选项呢 库文件和头文件在默认路径下 gcc 可以找到。 gcc 编译 C 代码 默认就应该链接 libc 库。 如果我们自己也不想使用这些选项呢 头文件和库文件分别拷贝到系统的默认路径下这个过程就叫做库的安装。我们之前学过环境变量 PATH然后写了个 Hello World让它不使用路径就可以执行其实是把可执行程序拷贝到环境变量的路径下其实就是安装可执行程序。 不过我们自己写的库叫做第三方库也就是除了系统语言之外的库。所以一般也要带上 -l name 表明你要链接的是哪一个库。 这里为什么不需要指定静态链接的方式 因为当前库中只有静态库所以这里就算不写 static也只能用静态的。也就是说有动态库和静态库时gcc 编译默认是动态库但只有静态库的时候就只能是静态库。 三、动态库的制作打包与使用使用 makefile
1、准备工作 这里打包动态库依旧不想把源文件暴露出去。 2、生成动态库 shared生成共享库格式 fPIC产生位置无关码position independent code库名规则libxxx.so 在栈区和堆区中间有一个共享区一般动态库的代码是映射在这个区域库文件当然也是一个文件当然也占磁盘空间。当程序运行其中需要执行库中的代码时本质就是进程来执行库中的代码。换而言之进程一旦运行起来同时也要把库加载到内存当然也可以局部加载然后就把库映射到共享区此时代码区的代码就可以直接访问共享区中的库。这样做的一个好处就是如果有多个进程时可以统一把要使用的一个库从内存映射到自己的共享区这样就相当于可执行程序是不需要携带库代码的从而可以有效的节省资源。 如果是静态链接形成进程后就没有用共享区此时代码区中就包含了你的代码和库的代码如果有 10 个 C 代码每个程序都把库代码都拷贝一份到代码区此时在内存中就会有 10 份重复的库代码而实际只需要 1 份即可。所以动态库最典型的特点就是所有和我们使用同一种库的进程可以把库从内存映射到共享区以节省内存资源。 其次有可能 A 进程共享区中只是一部分区域映射到 C 库B 进程共享区中也只是一部分区域映射到 C 库但不管最终物理到虚拟地址是如何映射的可执行程序加载到物理内存的任何位置最后都一定要保证库中产生的各种代码与我们这个库加载到内存中的位置和映射到共享区的位置是没有关系的这就叫做产生与位置无关码如果深入进一步了解就需要学习编译原理中可执行程序的格式。这里可以简单化的理解为因为库是随时随地可能加载的它也可能在内存中的任何位置也可能被映射到共享区的任何区域所以必须保证库中的代码不会出错。比如有一万行执行代码因为它本身与位置无关代码中出现了一个函数调用它在编译时地址是 0x1234加载到内存映射后函数的地址相对于调用方的地址发生了变化代码就可能执行不起来了所以必须保证调用目标函数的地址它本身是不会随着程序的加载位置以及映射区域的位置变化而变化的这就是产生位置无关码。 比如有一条跑道李四距离终点 80m张三距离终点 100m而当终点往前移动 20m 后此时李四和张三原来距离终点的距离就不对了但无论终点怎么变张三距离李四有 20m 是不变的这就是与位置无关码。 最后再说一下这个动态库是不会随着本身加载到共享区的任意位置而影响到库中代码地址发生变化而导致库中的代码不可执行的所以 gcc 一定要用 fPIC 选项来产生与位置无关码。 3、使用动态库
张三打包好库 output李四拿到了库 然后李四写好代码就开始编译这里如同静态库一样需要使用 -I-L-I 选项然后 ./main 时报错说不能打开共享文件原因是没有这个文件或目录为什么呢刚才不是很明显的把头文件在哪里库文件在哪里库文件的名字是什么告诉了 gcc怎么现在又不认识了
此时 ldd 确实也是有个库找不到刚才的选项是给编译器看的而此时 ./main 时已经和 gcc 没有关系了所以这里是运行时的问题所以在运行的时候也要能让系统帮我们找到运行时需要使用的动态库。
那为什么之前动态链接的其它程序可以直接运行呢因为运行时别人的库可以被找到它们在默认路径下。这里有一些方案这里只推荐这一种这里有一个环境变量 LD_LIBRARY_PATH在祼机器上是没有这个环境变量但如果你曾经做过 vim 配置可能就会有。如果想让你的库被运行起来需要把库路径导入其中。这里推荐导入绝对路径导入成功再 ldd发现这个库已经能找到了。此时就可以 ./main。 如果动静态库同时存在默认采用的是动态库。 如果动静态库同时存在但非要使用静态库呢 -static摒弃默认优先使用动态库的原则而是直接使用静态库的方案。 当目录下同时存在动静态库那么 gcc 在编译时默认就是动态链接而使用 static 后就是静态链接。 4、运行动态库 拷贝 .so 文件到系统共享库路径下一般指 /usr/lib 更改 LD_LIBRARY_PATH ldconfig 配置 /etc/ld.so.conf.d/ldconfig 更新 在动态库操作的时候需要把动态库所在的路径导入到环境变量 LD_LIBRARY_PATH但是重新登录时自己导入的环境变量的路径就没了。不推荐把变量导入到登录脚本
若想让它每次在重新登录上的时候不会被重新配置可以把库的路径导入到 /etc/ld.so.conf.d/ 目录下。这个配置文件是永久生效的导入成功后需要 ldconfig 刷新不过也不推荐这种方案原因是你写的库导入到这个目录下可能也会污染系统本身的环境变量信息。除非将来你需要导入第三方开源的库。 5、使用外部库 系统中其实有很多库它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数ncurses 库。 -lm 表示要链接 libm.so 或者 libm.a 库文件。 6、库文件名称和引入库的名称 如 libc.so - c 库去掉前缀 lib 去掉后缀 .so.a 为什么要有库 站在使用库的角度库的存在可以大大减少我们开发的周期提高软件本身的质量。站在写库的人的角度库既简单又安全。