说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
腾讯云-linux动态库和静态库、腾讯云-Linux 中的静态库和动态库简介及生成过程示例、腾讯云-详解Linux动态库生成与使用指南
CSDN-crazy_koala-linux下制作一个动态库、CSDN-lucky_白杨-C语言 共享库(动态库)制作
沐歌爱编程 -C语言丨静态库与动态库的区别,你知道多少?、腾讯云-浅谈Linux的动态链接库
上一篇:基础知识:篇2-多源文件编译过程
下一篇:基础知识:篇4-make工具与Makefile文件概念
在实际的软件开发中,对于一些需要被许多模块反复使用的公共代码,我们就将它们编译为库文件。
本质:一种可执行的二进制代码(但不可以独立执行),可被操作系统载入内存执行。
①静态库(static library):
在编译的链接步骤中,连接器将从静态库文件中取得所需的代码,复制到生成的可执行文件中。因此,整个库中的所有函数都被编译进了目标代码中。
静态链接后,执行程序中存在自己所需函数的一份拷贝
②动态库(共享库,shared library):
在编译的时候并没有被编译进目标代码中,而是程序执行到相关函数时才调用库中对应的函数。
动态链接后,执行程序仅仅是包含对共享库的一个引用。
不管是静态库,还是动态库,都是由*.o
目标文件生成的,都是一个obj文件的集合
对比 | 静态库 | 动态库 |
后缀名 | .a | .so |
生成工具 | ar工具 (archive) | gcc工具 (-shared) |
命名方式 | libxxxx.a ( xxxx =lib名) | libxxxx.so.major.minor (xxxx =lib名,major=主版本号,minor=副版本号) |
代码被载入的时刻 | 编译过程中被载入可执行程序 | 可执行程序运行时才载入内存 (编译过程中仅简单的引用) |
生成可执行程序体积 | 较大 | 较小 |
优点 | ①编译后的执行程序无需外部的函数库支持 (删除静态库对可执行程序无影响) | ①不同的应用程序若调用相同的库,那么内存里只需一份该共享库 ②动态库的改变不影响可执行程序【随时可升级库】 (共享对象接口相同,即可直接动态加载) |
缺点 | ①若静态库改变则程序需重新编译 | ①程序运行时才被载入,故运行时需动态库存在 |
静态库和动态库的最大区别:
静态库: 把库直接加载到程序中,所用存储空间大,因为执行程序中包含了库中代码拷贝;
动态库: 链接时它只是保留接口,将动态库与程序代码独立,可提高代码的可复用度,降低程序的耦合度。所用的运行空间大,因为它将不需要的代码也加载到运行空间。
若想不停止程序,升级动态库,不可直接cp 新动态库去覆盖旧动态库,而该采用:rm 旧+cp 新” 或“mv 旧+cp 新” 来替代直接“cp” 的操作方法。
Linux | Windows | |
可执行文件 | demo | <|
静态库 | libcommon.a | libcommon.lib |
动态库 | libcommon.so | libcommon.dll |
1.生成目标文件(中间文件):gcc -c xxx1.c xxx2.c ... -I 相对路径/绝对路径
2.生成静态库文件:ar -rcs xxx.a xxx1.o xxx2.o ...
将静态库文件编译进代码的指令:gcc xxx.c libxxx.a -I 相对路径/绝对路径 -o xxx
PS:
①若静态库不在该处可利用环境变量(
LIBRARY_PATH
):指定程序静态链接库文件搜索路径
②通过指令:-I 相对路径/绝对路径
:指定头文件路径。(需要编译的地方均需要【头文件在不在编译路径或环境变量路径中】)
③ar选项
-r replace,将objfile文件插入静态库尾或者替换静态库中同名文件
-x 从静态库文件中抽取文件objfile
-t 打印静态库的成员文件列表
-d 从静态库中删除文件objfile
-s 重置静态库文件索引
-v 创建文件冗余信息
-c create,创建静态库文件
1.生成目标文件(中间文件):gcc -fpic -c xxx1.c xxx2.c ... -I 相对路径/绝对路径
2.生成动态库文件:gcc -shared xxx1.o xxx2.o ... -o libxxx.so
将动态库文件编译进代码的指令:gcc xxx.c libxxx.so -L相对路径/绝对路径 -lxxx -I 相对路径/绝对路径 -o xxx
1.添加共享库路径,方式有:
方式一:将共享库置于/usr/lib
或/lib
方式二:添加该共享库路径为【共享目录环境变量】中。
法一:.bash_profile
文件下添加环境变量LD_LIBRARY_PATH及其路径
(指定程序动态链接库文件搜索路径)(重启后仍有效)
法二:export LD_LIBRARY_PATH及其路径
(重启后无效)
方式三:加/etc/f.f
文件(XXX
需要自己命名),把库所在的路径添加到文件末尾并执行ldconfig
刷新。 //对所用用户有效
2.执行可执行程序:./xxx
PS:
①可执行程序如何定位共享库?
需系统动态载入器 (dynamic linker/loader)
通过指令:-L 相对路径/绝对路径
:指定共享路径
通过指令:-l库文件名
:指定对应库,如:若需库:libxxxx.so.major.minor,则只需指令-lxxxx即可。
②gcc 选项:
-fPIC(或-fpic):表示编译为位置独立的代码。位置独立的代码即位置无关代码,在可执行程序加载的时候可以存放在内存内的任何位置。若不使用该选项则编译后的代码是位置相关的代码,在可执行程序加载时是通过代码拷贝的方式来满足不同的进程的需要,没有实现真正意义上的位置共享。
-shared:该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导>出符号),不用该标志外部程序无法连接。相当于一个可执行文件
ldd工具的作用:查看可执行程序依赖动态库
nm工具的作用:查看静态库和动态库中有那些函数名
nm列出的符号有很多, 常见的有三种:
T类:是在库中定义的函数,用T表示,这是最常见的;
U类:是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;
W类:是所谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。
ar工具的作用:生成静态库,同时可查看静态库中包含哪些.o文件(由哪些源文件构成)
生成静态库:ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o
-r?
查看静态库组成:ar -t libname.a
动态库:file *.so
静态库:objdump -x *.a
若有一个基础库libbase.a,还有一个依赖libbase.a编译的库叫libchild.a;
编译程序时,一定要先-lchild再-lbase。
若使用 -lbase -lchild,在编译时将出现一些函数undefined,而这些函数实际上已经在base中已经定义;
过程 | 静态库 | 动态库 |
生成目标文件(中间文件) | gcc -c add.c sub.c -I ./include/ | gcc -c -fpic add.c sub.c -I ./include/ |
打包目标文件生成库 | ar -rcs libmymath.a add.o sub.o | gcc -shared add.o sub.o -o libmymath.so |
源文件链接库生成可执行文件 | gcc mymath.c libmymath.a -I ./include/ -o mymath | gcc mymath.c libmymath.so -L ./ -lmymath -I ./include/ -o mymath |
运行可执行文件 | 直接运行 | 需将生成的动态库复制到共享库路径下再运行 |
第一步:创建文件
在/home/xsndz/mymath下创建文件:mymath.c、add.c、sub.c
在/home/xsndz/mymath/include下创建文件:add.h、sub.h
1️⃣mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));return 0;
}
2️⃣add.c文件
#include "add.h"int add(int a, int b)
{return a + b;
}
3️⃣add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);#endif
4️⃣sub.c文件
#include "sub.h"int sub(int a, int b)
{return a - b;
}
5️⃣sub.h文件
#ifndef _SUB_H_
#define _SUB_H_int sub(int a, int b);#endif
第二步:编译源文件生成目标文件(中间文件)
输入指令:gcc -c add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
第三步:将利用ar工具将目标文件打包为静态库
输入指令:ar -rcs libmymath.a add.o sub.o
生成静态库: libmymath.a
第四步:源文件链接静态库生成可执行文件【需指定头文件】
输入指令:gcc mymath.c libmymath.a -I ./include/ -o mymath
生成可执行文件:mymath
第五步:运行执行文件mymath
输入指令:./mymath
文件大小:8760
验证:
1️⃣修改mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));add_printf(a,b);return 0;
}
2️⃣修改add.c文件
#include<stdio.h>
#include "add.h"int add(int a, int b)
{return a + b;
}int add_printf(int a, int b)
{return printf("%d + %d = %dn",a,b,a+b);
}
3️⃣编译文件
输入指令: gcc -c add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
4️⃣目标文件打包为静态库
输入指令:ar -rcs libmymath.a add.o sub.o
生成静态库:libmymath.a
5️⃣源文件链接静态库
输入指令: gcc mymath.c libmymath.a -I ./include/ -o mymath
【需指定头文件】
输出报错信息:
解决方案:修改add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);
int add_printf(int a,int b);
#endif
1️⃣修改mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));add_printf(a,b);return 0;
}
2️⃣修改add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);
int add_printf(int a,int b);
#endif
3️⃣编译文件
输入指令: gcc -c add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
4️⃣目标文件打包为静态库
输入指令:ar -rcs libmymath.a add.o sub.o
生成静态库:libmymath.a
5️⃣源文件链接静态库
输入指令: gcc mymath.c libmymath.a -I ./include/ -o mymath
【需指定头文件】
输出报错信息:
原因:链接过程失败,没有链接到该函数的相关定义。
解决方案:修改add.c文件
#include<stdio.h>
#include "add.h"int add(int a, int b)
{return a + b;
}int add_printf(int a, int b)
{return printf("%d + %d = %dn",a,b,a+b);
}
①只要有编译的过程,就需头文件!(预编译时需要需要加载进去,没有则报错)
②编译过程(这边指的是:预编译、编译、汇编,未包含链接)不会报错,但链接时会报错。
1.头文件可不用全部声明源文件中的函数【头文件本身只需声明需要对外接口的函数列表即可】,但是链接时会发现没有声明该函数就使用,故链接会报错)
2.头文件声明的函数接口在源程序中未定义。编译过程不会报错,只有当要进行链接操作时,才链接失败,发现原来该函数还未定义。
第一步:创建文件
在/home/xsndz/mymath下创建文件:mymath.c、add.c、sub.c
在/home/xsndz/mymath/include下创建文件:add.h、sub.h
1️⃣mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));return 0;
}
2️⃣add.c文件
#include "add.h"int add(int a, int b)
{return a + b;
}
3️⃣add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);#endif
4️⃣sub.c文件
#include "sub.h"int sub(int a, int b)
{return a - b;
}
5️⃣sub.h文件
#ifndef _SUB_H_
#define _SUB_H_int sub(int a, int b);#endif
第二步:编译源文件生成目标文件(中间文件)
输入指令:gcc -c -fpic add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
第三步:将利用gcc工具将目标文件打包为动态库
输入指令:gcc -shared add.o sub.o -o libmymath.so
生成动态库: libmymath.so
第四步:源文件链接动态库生成可执行文件【需指定头文件】
输入指令:gcc mymath.c libmymath.so -L ./ -lmymath -I ./include/ -o mymath
生成可执行文件:mymath
第五步:添加共享库路径
输入指令:cp libmymath.so /usr/lib
将生成的动态库libmymath
复制到 /usr/lib下。
第六步:运行执行文件mymath
输入指令:./mymath
文件大小:8720
验证:
1️⃣修改mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));add_printf(a,b);return 0;
}
2️⃣修改add.c文件
#include<stdio.h>
#include "add.h"int add(int a, int b)
{return a + b;
}int add_printf(int a, int b)
{return printf("%d + %d = %dn",a,b,a+b);
}
3️⃣编译文件
输入指令: gcc -fpic -c add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
4️⃣目标文件打包为动态库
输入指令:gcc -shared add.o sub.o -o libmymath.so
生成静态库:libmymath.so
5️⃣源文件链接静态库
输入指令: gcc mymath.c libmymath.so -L ./ -lmymath -I ./include/
【需指定头文件】
输出报错信息:
解决方案:修改add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);
int add_printf(int a,int b);
#endif
1️⃣修改mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"int main(int argc, char **argv)
{int a = 10 ,b = 5;printf("%d + %d = %dn",a,b,add(a,b));printf("%d - %d = %dn",a,b,sub(a,b));add_printf(a,b);return 0;
}
2️⃣修改add.h文件
#ifndef _ADD_H_
#define _ADD_H_int add(int a, int b);
int add_printf(int a,int b);
#endif
3️⃣编译文件
输入指令: gcc -fpic -c add.c sub.c -I ./include/
【需指定头文件】
生成目标文件:add.o、sub.o
4️⃣目标文件打包为动态库
输入指令:gcc -shared add.o sub.o -o libmymath.so
生成动态库:libmymath.so
5️⃣源文件链接动态库
输入指令: gcc mymath.c libmymath.so -L ./ -lmymath -I ./include/ -o mymath
【需指定头文件】
输出报错信息:
原因:链接过程失败,没有链接到该函数的相关定义。
解决方案:修改add.c文件
#include<stdio.h>
#include "add.h"int add(int a, int b)
{return a + b;
}int add_printf(int a, int b)
{return printf("%d + %d = %dn",a,b,a+b);
}
与静态库差不多,但动态库生成的可执行文件不可直接运行(当然动态库还有很多其他优点)
本文发布于:2024-01-29 02:19:15,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170646596212024.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |