【遗留问题】 makefile 写法;
1. linux/windows 动态库和静态库区别:
linux: | windows | |
动态库名称 | xxx.so (201806做的项目,使用linux动态库,这样目标程序比较小; 如何在makefile中增加动态库的调用) | xxx.dll |
静态库名称 | xxx.a | xxx.lib |
2. 静态库和动态库区别:
1)最大区别: 加载的时刻不同
静态库在程序编译阶段加载,可能使目标程序变大
动态库在程序运行阶段加载,目标程序相对较小。
2)库文件是否可删除
静态库编译完成可以删除库文件
动态库编译完成必须保留库文件
3)制作库文件命令不同
接口文件一致,创建目标文件过程一致,但 制作库文件命令不同,使用流程不同
静态库: ar crs libhello.a hello.o
动态库: gcc -shared -fPIC hello.o -o libhelllo.so (-fPIC,表示-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code))
4)不论是静态库还是动态库都存在潜在的隐患。
(不知库文件的函数功能是否有木马或病毒,或其他函数操作)
3. 静态库的制作:
1)编写功能函数 ==》功能实现 ==》不能有main函数
hello.c ==》只做一个hello功能。
hello.h ==》接口文件,用于声明功能函数。
2)编译功能函数为目标文件:
gcc -c hello.c -o hello.o
3)使用目标文件制作静态库:
ar crs libhello.a hello.o
注意:目标静态库必须以lib开头,以.a 结尾。
4)使用静态库:
4.1 编写测试单元main.c
4.2 将测试代码与库同时编译
gcc main.c -L. -l hello (-L. :表示当前路径; -l :后面跟库文件名字;)
4.3 验证库的执行效果
./a.out ==> hello xxx
备注:
-L:
-l
3.1 静态库简单应用:
编写将printf打印函数,打印一个字符串,并制作一个静态库文件;(程序中,只能1个main函数,接口函数只提供接口,供其他函数调用。)
如下是main函数:
[root@llz test]# cat main.c
#include "hello.h"
int main(int argc, const char *argv[])
{
char * name = "zhangsan";
hello(name);
return 0;
}
如下是打印接口函数,只提供接口,接口函数声明在.h文件中,该.c 文件中不能包含main函数。
[root@llz test]# cat hello.c
#include <stdio.h>
int hello( char * name)
{
printf("hello %s \n",name);
return 0;
}
[root@llz test]# cat hello.h
#ifndef _HELLO_H_
#define _HELLO_H_
int hello(char *name);
#endif
编译生成的文件:
-rwxr-xr-x. 1 root root 4802 6月 23 15:59 a.out
-rw-r--r--. 1 root root 99 7月 28 2016 hello.c
-rw-r--r--. 1 root root 70 7月 28 2016 hello.h
-rw-r--r--. 1 root root 868 6月 23 15:58 hello.o
-rw-r--r--. 1 root root 1010 6月 23 15:58 libhello.a
-rw-r--r--. 1 root root 153 8月 6 2017 main.c
4. 动态库制作(共享库):
1)编写功能函数
hello.c
hello.h
2)将功能函数编译成目标文件
gcc -c hello.c -o hello.o 【命令有时会执行失败】
3)制作动态库
gcc -shared -fPIC hello.o -o libhello.so
4)使用测试环境测试动态库
4.1 )编写main.c 测试代码
4.2 )拷贝目标动态库到系统路径
cp libhello.so /usr/lib
或 export LD_LIBRARY_PATH=/mnt/nfs/1615/io/06
4.3 )编译测试单元
gcc main.c -L. -l hello
4.4 )执行测试单元
./a.out ==>hello xxx
注意:
1)动态库有时候有版本要求:
so.xx.yy
xx 代表主版本
yy 代表副版本
2)如下语句表示定义自己的库文件到指定目录:
export LD_LIBRARY_PATH=/mnt/nfs/1615/io/06
4.1. 动态库简单应用:
定义一个结构体,计算并打印出该结构体的大小;注意:打印函数使用动态库实现;
1)编写打印功能函数:func_file.c
#include <stdio.h>
#include "func_file.h"
int func_printf(int len)
{
if (len < 0) {
printf("input len is invalue, len:%d\n", len);
return 1;
}
printf("len = %d\n", len);
return 0;
}
2)编写打印功能函数头文件:func_file.h
#ifndef FUNC_FILE_H
#define FUNC_FILE_H
int func_printf(int len);
#endif
3)编译功能函数,并生成动态库:libfunc_file.so
编译目标文件后,由目标文件生成动态库时失败了。。。如下图所示:
解决办法编译器已经提示了:recompile with -fPIC
但是我们应该重新编译谁带上这个参数呢?经过我几番折腾,发现如下情况:
1、编译.o文件的时候,没有加参数-fPIC,这问题个在gcc version 3.4.6版本没有发生,可能那个编译器默认都给加上吧。
2、当前程序中有用到某个静态库,那个静态库编译.o文件的时候没有加上-fPIC(静态库其实就是.o文件打包)。补充一点:我发现手写Makefile时即使不加-fPIC也没有这个问题,这一点很郁闷,这个只在用automake工具编译出.a文件时才出现过。
知道原因了,解决办法自然有了,保证你编译.o文件的时候,都加上-fPIC,这样你才能编译出动态库来。
创建 PIC 的编译器标志是 -fPIC.创建共享库的链接器标志是 -shared。
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。
这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
4)动态库的使用:即在 main.c 函数中调用
链接动态库编译 主函数后,可执行文件 a.out 无法执行(如下图所示):
原因:是因为系统没有找到库文件,系统默认是去/lib和/user/lib中寻找动态库文件;
https://blog.csdn.net/rainbowchou/article/details/78220581
解决方法有三个:
一)将库文件放在系统默认寻找的目录下,即/lib和/user/lib下(不建议)
二)临时改变环境变量来增加路径,新开的shell就会失效
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
三)改变配置文件来增加路径,一直可用
有两种方法可达成,选一即可。
1、在终端上输入sudo gedit ~/.bashrc,在最后一行加入 export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
保存退出,终端执行Source ~/.bashrc,重新加载脚本文件。
使用echo $LD_LIBRARY_PATH,可看环境变量是否添加成功。
再次执行程序,应该就没问题了
2、在/etc/ld.so.conf.d里添加一个xxx.conf文件(xxx为自定义的名字);
xxx.conf中添加库文件的存放目录,保存退出后,在终端输入以下命令进行刷新:
sudo ldconfig
现在执行程序也应该没问题了
使用 export 的方式实现:
总结:
无论是动态库还是静态库,从用户使用角度来说,都只是一个接口函数,调用该接口可完成相应的功能;使用者不需过多的关注库接口函数的实现,只需关注接口函数及形参即可;使用库函数优点:
一方面是方面,分模块开发时,使功能模块具有强独立性;
另一方面,在比较大的工程中,某些模块复用复用比较多,则可封装成动态库的形式,在程序运行阶段调用,这样可节省编译的内核尺寸;
还要就是防止泄漏本公司的代码给其他公司,也封装成动态库或静态库的形式给客户。
Linux-C动态库与静态库的编译与调用