做营销网站多少钱,天动力网站开发,哪些网站做家政推广,做兼职女的网站动静态库
头文件与库的区别 头文件一般而言#xff0c;是声明和宏定义。头文件是在预处理阶段使用的 库文件是已经编译好的二进制代码。是一种目标文件#xff0c;库文件是在链接阶段使用的 对于头文件和库我们可以这样理解#xff0c;就是头文件提供的是一个函数的声明是声明和宏定义。头文件是在预处理阶段使用的 库文件是已经编译好的二进制代码。是一种目标文件库文件是在链接阶段使用的 对于头文件和库我们可以这样理解就是头文件提供的是一个函数的声明并没有这个函数的具体代码而库就是存放这个函数的具体实现代码
我们在写代码的时候经常依赖别人给我们提供的函数比如头文件中的reverse函数我们在写的时候这个头文件中包含有这个函数的声明而这个函数的定义是存在于库中的使用库的意义在于我们可以将函数的定义直接编译成二进制使得别人看不见这个函数的具体实现方法别人使用时只需要根据声明中的说明即可使用这样使得软件作者既实现了发布方便或替换方便方便程序的部署与开发。也方便了一些发布者不想让别人看到自己写的函数的具体实现方法保护了代码隐私。当然库中肯定不止有函数的定义这里只是举了一个简单的例子方便理解。
库的种类
库文件是已经编译好的二进制代码这个二进制代码可以是动态的如.so也可以是静态的如.a
如果是动态的则最后生成的程序文件在运行时需要这个动态库的支持动态库又叫做共享目标文件
如果是静态的则最后生成的 可执行程序文件运行时可以脱离这个库文件而独立运行。 静态库又叫做可重定位目标文件 静态库.a程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库.so程序在运行的时候才去链接动态库的代码多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表而不是外部函数所在目标文件的整个机器码在可执行文件开始运行以前外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中这个过程称为动态链接dynamic linking动态库可以在多个程序间共享所以动态链接使得可执行文件更小节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用节省了内存和磁盘空间 目标文件
在生成我们的可执行程序之前我们的程序一共会经过四个阶段预处理编译汇编链接。
在经过前面三个阶段后汇编阶段会生成所谓的目标文件windows下为.obj文件Linux下为.o文件目标文件是还没经过链接的文件也就是说此时的目标文件中有些符号还未被调整成正确的有效地址仍然临时地址如何理解呢可以理解成此时你写的程序内使用的reverse函数中还只是只有声明的部分需要通过找到reverse存在的库的目标文件链接起来这样才能成功调用reverse函数。
PC端的可执行文件格式主要包括了windows下的PE(Portable Execute)以及linux下的ELFExecute Linkable Format而目标文件属于生成可执行程序文件过程中的中间文件其格式几乎与可执行程序的文件一模一样因此可以看成是一种类型的文件只不过是需要再通过最后一个阶段链接阶段将目标文件中各种各样的段合并起来本质上文件格式并没有改变什么想要深入了解可以参考一下这篇博客目标文件详解而我们的ELF格式的文件也是有分类型的可重定位文件relocate file可执行文件executable file共享目标文件shared object file ELF文件格式类型 可执行文件 Linux下和windows下的.exe文件也就是可以直接执行的文件 可重定位文件 windows下的.obj文件和Linux下的.o文件也就是需要通过链接后形成可执行程序的目标文件 共享目标文件 动态链接文件可以和其他可重定位文件和共享目标文件一起链接生成新的目标文件 使用readelf -a filename 可以查看目标文件的ELF格式 relocatable重定位 executable可执行 静态库的特征、包装与使用
特征 静态库.a程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库如果是静态的则最后生成的 可执行程序文件运行时可以脱离这个库文件而独立运行。 静态库又叫做可重定位目标文件 因为静态库是直接把依赖的库的代码链接到可执行文件中也就是在进行链接一步时把库中需要的内容直接拷贝一份到最终的可执行文件中比如说你在自己的代码中写的reverse函数头文件中是只有声明的链接一步时 需要进行合并符号表而reverse函数的定义存在于库中也就是需要合并库的符号表若选择了静态库即静态链接方式就是把库中的符号表拷贝一份进可执行文件中使得可执行文件的大小会明显地变得非常大。 静态库的优点 加载快可以独自运行 由于库中的内容已经被你拷贝进了可执行文件中因此可执行程序执行时不再需要任何依赖自己就包含了声明与定义随时都可以自己执行。 静态库的缺点 占用空间大 这不仅仅体现在可执行文件的大小上若多个源文件都是以静态链接的方式去链接同一个静态库那么一模一样的代码就会被拷贝多次重复加载使得磁盘空间也被重复占用。 兼容性与拓展性不佳 由于静态链接是将静态库拷贝进自己的代码中那么当库中某一个函数的实现被修改而可执行文件中保存的是旧的代码这样就使得可执行文件又要再一次重新编译链接不方便程序的升级迭代与重新部署。 包装 静态库形成步骤 先写好 .c文件和 .h 文件 将 .c 文件编译成目标文件 使用ar命令生成静态库 库名字前缀必须是lib -rc - r(replace) c(creat) 查看静态库中目录列表 t:列出静态库中的文件 v:verbose 详细信息 将头文件和库文件打包起来 将所有的头文件放在include目录下静态库文件放在lib目录下再将include目录和lib目录放在同一个目录下这样将此目录发送给其他人即可使用 可以使用Makefile文件一次性全部生成 使用 使用将mylib文件夹放在main函数所在的目录下 gcc编译器默认会先在当前路径下搜索头文件然后再去库路径下搜索所以需要显性的告诉编译器头文件的路径在哪使用 -I 选项 还需要告诉编译器库的路径 使用 -L 选项 以及告诉编译器库的名字使用 -l 选项使用-l选项时去掉前缀 lib 和后缀 .o 即可得到库文件名 还可以直接将我们的库直接拷贝到系统路径下这样编译时就不再需要指定路径编译器会自动到系统路径下找 但不建议这么做由于自己的库没有经过可靠性验证尽量不要安装到系统路径下污染库文件 问题 使用file文件 查看可执行程序所使用的链接方式 ldd文件命令 查看可执行程序所依赖的库文件 这就奇了怪了为什么会是动态链接呢我们使用ldd查看一下所依赖的库是什么 我们查看一下所依赖的这个库文件的类型 原来是一个共享目标文件类型但我们已经测试过此可执行程序是可以运行的也就是说确确实实是已经使用了我们自己写的静态库了那究竟是怎么回事呢 其实一切都要看我们写的程序中所需要依赖的库由于我们的程序中存在stdio.h库中的printf函数因此是需要用到C语言库的而我们编译时指定库的路径时并没有指定C语言库的路径只指定了我们所需要的Add函数和Print函数所对应的库的路径也就是说没有指定路径时编译器默认是用动态链接的方式使用C语言中的动态库若我们强行要将所有的依赖文件全部改为静态链接方式也可以带上选项 -static 我们可以发现用这种方式将C语言的静态库文件以静态链接方式加载到可执行文件main中使得main的文件大小非常之大但我们所用到的仅仅只有printf函数这样就造成了空间的浪费 比较一个静态库一个动态库的方式下的文件大小可以发现差距已经达到万级因此C语言库的文件还是以动态链接的方法合适不到万不得已不使用静态链接 动态库的特征、包装与使用
特征 如果是动态的则最后生成的程序文件在运行时需要这个动态库的支持动态库又叫做共享目标文件动态库.so程序在运行的时候才去链接动态库的代码多个程序共享使用库的代码。 静态库是库的代码与自己的代码一起在代码区来回跳也就是执行可执行程序时就把库代码拷贝一份到自己的代码区了。动态库是一开始在硬盘中执行到需要库的代码时再将库加载进内存中然后将库中对应所需的函数等方法建立与页表的映射关系映射到共享区当中然后代码区执行的时候再跳转到共享区: 动态库的优点: 节省磁盘空间,占用空间小 动态链接只需要将动态库的内容加载一次到内存中,若多个文件同时都依赖此库也不需要加载多次,并且只需要加载一些符号表信息与相对路径信息即可,不会占用可执行文件的大小空间 兼容性佳,拓展性强 还是以reverse函数的例子,如果reverse函数的实现改变了,那么只需要改变动态库本身即可,不需要重新编译执行可执行文件,因为他记录的仅仅是他的信息,通过信息去查找函数,若库本身就已经变了,那么下一次通过此信息去寻找时,找到的也是更新后的内容,不影响更新迭代,因此动态库大大方便了程序的升级和部署 动态库的缺点: 依赖性 由于静态链接的方式是直接将静态库文件加载进自己的可执行文件,因此下一次执行时不管静态库文件还存不存在都不影响使用,但动态链接不一样,由于可执行文件记录的是动态库信息,可以理解成记录的是如何找到动态库在内存中的位置,因此若动态库不存在了,可执行文件也无法正常运行了,具有强依赖性! 复杂性高 由于动态库的特殊链接方式,因此还需要设计动态链接器等等复杂的技术来辅助,而不像静态库一样粗暴,因此具有一定设计复杂性 包装 动态库形成步骤:直接用Makefile展示最后结果,若不明白回去静态库的形成再吃一遍 先写好.c .h 文件 将.c 文件以fPIC生成无关码方式编译形成.o文件,作用:告诉编译器此目标文件在任何路径都无所谓 直接用编译器使用 -shared选项生成动态库.so 文件 将.h 文件和 .so 文件包装起来 使用到的命令: 展示:
使用 还是跟静态库使用一样,先把包装好的目录放置main函数所在的目录下,并使用gcc编译器编译时指明头文件路径,库路径以及动态库名字: 问题 但我们发现,此时可执行程序无法运行,并且提示原因是找不到此共享文件目标,这是怎么回事呢?我们再一次通过ldd文件名来查看 此时我们发现提示的是找不到对应的动态库,但我们不是已经指明了路径了吗?这是为什么? 其实,我们使用 -I -L -l选项时,其实是告诉编译器路径,但一旦可执行程序编译完毕,编译器就做完了他自己的事情了,我们要运行可执行程序时,就与编译器没有关系了,而是操作系统的事情,操作系统此时就找不到路径了,静态库不用找是因为库中代码已经被拷贝进自己的代码中了,并不需要再去找了, 所以为了让操作系统能够顺利找到并执行,有以下方法: 拷贝.so文件到系统共享库路径下,一般在/lib64 成功正常运行 使用环境变量LD_LIBRARY_PATH 但这种属于内存级别的环境变量,在下一次重启时就会被清掉了: 35)]
[外链图片转存中…(img-55lGQr3v-1707561573535)]
成功正常运行 使用环境变量LD_LIBRARY_PATH [外链图片转存中…(img-A6itzueN-1707561573535)]
但这种属于内存级别的环境变量,在下一次重启时就会被清掉了:
[外链图片转存中…(img-yaa4exwB-1707561573536)]