【静态连接和动态连接】C/C++编程中的两种有效链接策略_c++ 动态链接 静态链接_SecureCode的博客-CSDN博客
静、动态库概念和各自优点
静:
动:
动态库:只有一份,运行时具体代码行才加载使用(相对慢);
静态库:编译时候一块编进去,用几处编几份,执行速度快场景。
从静态到动态是一个时间换空间的过程。
静态库制作、使用以及gcc常见报错处理
将几个内涵若干个函数的.c文件,先各自处理为 .o文件。
然后,执行静态库制作的命令。生成的 .a 文件即为制作好的静态库文件了。
注意:库的命名必须以 lib 开头,静态库要以 .a 结尾
使用静态库(把库和调用文件一块编译即可生成 .out可执行文件即可);
gcc报错,一般有两种阶段多见,编译和链接。
报错,有行号,说明是编译阶段报错,一般也是语法检查出错了;
没行号,说明已经是二进制了,是链接阶段报错。
根据上面的使用方法,给gcc 再加一个 -Wall 参数,出现告警信息。分析,下面的报错显然是编译阶段:
原因:显然是调用文件.c中没有声明 使用的库函数;
解决办法:再另外做一个静态库头文件。
放在编译文件所在目录下,再执行原来的编译命令,就没问题了。
加强:静态库制作的时候,注意另外给静态库.a制作一个.h头文件。然后在主函数里面include里面加。完事在头文件所在目录下执行目标文件的编译命令。避免编译器帮你隐式声明而导致的不必要的问题
万恶之源:C语言中的隐式函数声明_隐式声明-CSDN博客1 什么是C语言的隐式函数声明在C语言中,函数在调用前不一定非要声明。如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码。下面是一个例子:int main(int argc, char** argv){ double x = any_name_function(); return 0;}单纯的编译上述源代码,并没有任何报错,只是在链接阶段因为找_隐式声明https://blog.csdn.net/smstong/article/details/50523120?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-6-50523120-blog-124943920.235%5Ev38%5Epc_relevant_anti_vip_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-6-50523120-blog-124943920.235%5Ev38%5Epc_relevant_anti_vip_base&utm_relevant_index=11
动态库的制作及使用(与静态库大体相同,特别注意区别):
动态库的制作
注意和静态库的关键区别:
动态库是只有调用到相关方法时,相关库才被加载到内存中被使用。而静态库是在编译时候,静态方法在代码里的位置已经相对main函数确定了。因此,基于这一点区别动态库在制作时,制作库的相关命令参数 稍有不同【要生成与位置无关的代码 借助参数 -fPIC】。
代码:
//add.c int add(int a, int b) { return a+b; }
//sub.c int sub(int a, int b) { return a-b; }
//test.c文件 //不需要包含add.c和sub.c文件,也可以加进行编译工作,但是会提示错误 #include <stdio.h> int main(int argc, char** argv) { int a=20,b=10; printf("a+b=%d\n",add(a,b)); printf("a-b=%d\n",sub(a,b)); return 0; }
//库头文件 #ifndef _CAL_H #define _CAL_H int add(int, int); int sub(int, int); #endif
1.分别将 .c 库函数文件生成 .o文件;
动态库要求生成与位置无关的代码(函数调用之前需要将其地址固定)
数据段合并和地址回填 延迟绑定(动态库函数的地址比主函数的其它调用函数分配地址要晚)
结论:制作动态库的.o文件和静态库有区别,生成位置无关文件,借助 -fPIC选项
gcc -c add.c -o add.o -fPIC
2.使用gcc 和 -shared选项将所有 .o库文件制作成一个动态库文件gcc -shared -o lib库名.so add.o sub.o
3.编译可执行程序时,指定所使用的动态库, -l和-L-l:用来指定库名 -L:用来指定库路径
gcc test.c -o a.out -lcal(cal是库名) -L./lib
至此,包含了库的可执行文件就生成了。下面开始执行代码:
(执行过程中会有问题,就是库钓不上,因此还有一步要补充,后面会提到)
4.运行可执行程序;
./a.out
————————————————
版权声明:本文为CSDN博主「CPPlusQt」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Blunt_Du/article/details/122329243
动态库路径环境变量的添加
按照上述方法,制作一个动态文件执行后,
报错:
原因:
是找不到自己在某个目录下做的动态文件。准确地说,是程序执行的时候 动态连接器 没有找到程序中使用的动态库。(注意这句话要和制作动态库的时候“链接器”指定动态库目录区分开,完全不同阶段的东西)
概念区分:
链接器 (编译阶段):工作于链接阶段,工作时需要 -l和-L选项(用于编译静态库)
动态链接器 (运行阶段):工作于程序运行阶段,工作时需要提供动态库所在目录位置;
可以使用ldd命令--查出可执行文件依赖了哪些动态库:
ldd是list, dynamic, dependencies的缩写, 意思是, 列出动态库依赖关系。
Linux中的8个ldd命令示例_ldd -r-CSDN博客
看上面的依赖显示,c库其实也是一个动态链接库
解决办法:
想办法让 a.out执行的阶段,能找到它使用的so库。即需要适时指定一个路径。
具体解决方案:
解决方法:
方案1:临时修改该用户下 bash的环境变量
程序运行时候,会由一个环境变量(里面存了很多路径)指向动态库在哪。我们在当前shell临时更新下这个环境变量即可。
#环境变量生效: export LD_LIBRARY_PATH=./lib #上面填入的是相对路径,如果要一直生效,最好填入绝对路径 #运行程序 ./a.out
存在不足:环境变量是依赖于终端的,切换终端之后,新的终端的环境变量会失效
linux 环境变量https://mp.csdn.net/mp_blog/creation/editor/132511548LD_LIBRARY_PATH用法详解-CSDN博客LD_LIBRARY_PATH_ld_library_pathhttps://blog.csdn.net/m0_58235748/article/details/130557000?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169699099116800182196576%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169699099116800182196576&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-130557000-null-null.142%5Ev95%5Einsert_down28v1&utm_term=LD_LIBRARY_PATH&spm=1018.2226.3001.4187
方案2:修改该用户下 bash的环境变量配置文件:(1) 通过gedit或者vi修改bash的配置文件; #脚本的名字为.bashrc gedit ~/.bashrc #或者使用vim打开 vi ~/.bashrc (2)在终端中添加动态库路径 export LD_LIBRARY_PATH=动态库路径 建议使用绝对路径 (3)使脚本文件生效,通过运行脚本文件实现、 . .bashrc #或者 source .bashrc #或者重启终端,每次重启终端,bashrc都会运行
方案3:拷贝自定义动态库到标准C库(不推荐)标准C库也是通过动态库进行加载的,而且可以加载成功,这给我们的提示是可以把这个生成的动态库拷贝到C库的文件夹中 sudo cp libmymath.so /lib 然后把原来设置的环境变量删掉。
方案4:配置文件法
#打开配置文件 sudo vi /etc/ld.so.conf 在配置文件中写入库文件目录所在位置,最好写入绝对目录 让配置文件生效 #-v选项会在终端显示动态库的加载位置 sudo ldconfig -v 运行程序生效
总结四种方法:
————————————————
原文链接:https://blog.csdn.net/Blunt_Du/article/details/122329243
可以通过下面的命令查看运行程序运行之后,需要加载那些动态库,以及动态库的路径
ldd a.out
知识补充:在静、动态库这块知识方面,gcc还需要补充学习几个参数
gcc - 参数
-I ( i 大写) :指定头文件路径(相对路径或觉得路径,建议相对路径)
-i :指定头文件名字 (一般不用,而是直接放在**.c 文件通过#include<***.h> 添加)
-L :指定连接的动态库或者静态库路径(相对路径或觉得路径,建议相对路径)
-l (L小写) :指定需要链接的库的名字(链接 libc.a :-lc 链接动态库:libc.so : -lc 注意:-l后面直接添加库名省区“lib”和“.so”或“.a” )
————————————————
原文链接:https://blog.csdn.net/abcdu1/article/details/86083295