树莓派开发——Linux静态动态库
文章目录
- 树莓派开发——Linux静态动态库
- 一、分文件编程
- 1.1 分文件编程的优点:
- 1.2 分文件编程的步骤:
- 二、Linux的库
- 2.1 函数库的概念:
- 2.2 静态库和动态库的比较:
- 静态数据库(libXXX.a)
- 优点
- 缺点
- 动态数据库(libXXX.so)
- 优点
- 缺点
- 2.3 静态库的生成和使用:
- 2.3.1 静态库的制作步骤:
- 2.3.2 静态库的使用:
- 2.4 动态库的生成和使用:
- 2.4.1 动态库的制作步骤:
- 2.4.2 动态库的使用:
一、分文件编程
在之前的学习中,面对较大的项目比如 香橙派实现的智能垃圾桶等 ,都使用了分文件编程的思路。其实现的核心思想就是:将功能性函数的实现单独写在其他的地方,在main函数中调用那些封装好的功能性函数。
1.1 分文件编程的优点:
- 分模块的编程思想:在实际工作中面对大型项目,可以让A完成串口开发;B完成网络开发,最后只需要他们提供h文件中的函数接口就可以在主函数中直接调用了,测试时发现哪部分有问题可以直接找负责的人,方便调试
- 代码可移植性更强:因为分文件编程了,串口,网络,语音可能都被封装好了,那么后续如果其他项目需要这些功能就可以直接调用封装好的接口了,最多只需要微调
- main函数更加精简:由于把功能性函数的实现步骤都封装到其他文件了,main函数就可以专注于项目的整体调用逻辑,使得整个main看起来更加清晰,逻辑通畅
1.2 分文件编程的步骤:
- 将功能性函数名和具体实现步骤写在一个**.c**文件中
- 创建一个同名的.h文件,包含所有可能会被调用的函数原型,去除函数体
- 在main中包含刚刚创建的**.h**文件
- 在main中调用被封装好的函数接口
- 使用gcc 编译所有相关的**.c文件**(封装函数的.c文件;main函数所在的.c文件)
- test.h文件的格式:
#ifndef __TEST_H__
#define __TEST_H__
int add(int x, int y);
int min(int x, int y);
float div(int x, int y);
#endif
- test.c文件的格式:
int add(int x, int y)
{
//函数体
}
int min(int x, int y)
{
//函数体
}
float div(int x, int y)
{
//函数体
}
- main函数调用h文件格式:
#include <stdio.h>
#include "test.h"
int main()
{
//函数体
return 0;
}
二、Linux的库
分文件编程的好处已经在刚刚说到,但是实际工作中会出现这种情况:程序员允许别人可以调用他封装好的功能性函数,但是他不希望别人可以看到他具体实现的函数体。在这种情况下,就要引入Linux的库的概念了!
2.1 函数库的概念:
库(程序函数库)是一种可执行的二进制形式、就是将源代码转化为二进制格式,相当于进行了加密,别人可以使用库,但是看不到库中的内容。
库是别人写好的现有的,可以复用的代码,现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries):
- 静态函数库:在程序执行前就加入到目标程序中的库, 文件后缀是.a
- 共享函数库:在程序执行时动态(临时)由目标程序去调用,共享函数库=动态函数库=共享对象库(Linux), 文件后缀是.so
- 动态加载函数库:本质上和共享函数库是一个东西,“动态加载数据库”是windows中的叫法,文件后缀是.dll
因此,对于Linux系统来说可以简单的将库分为 动态库 和 静态库
2.2 静态库和动态库的比较:
静态数据库(libXXX.a)
优点
- 运行快
- 发布程序无需提供静态库,因为已经在app中,移植方便
缺点
- 程序大
- 更新部署发布麻烦
动态数据库(libXXX.so)
优点
- 程序小
- 升级简单
- 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享(动态)库的实例
缺点
- 运行相对慢
- 需要提供依赖的动态库
2.3 静态库的生成和使用:
2.3.1 静态库的制作步骤:
- 使用以下指令将**.c文件生成.o文件**
gcc a.c b.c -c
- 使用以下指令将**.o文件打包成.a库文件**
ar rcs 静态库的名字 原材料
例:ar rcs libXXX.a a.o b.o
这两步完成后,就生成了.a库文件,此时实现功能函数的.c文件和.o文件对于程序运行就不必要了,使得main函数可以调用这个库的条件就是有.h和.a文件,此时代码执行者可以调用库但却无法得知库中函数具体的实现步骤了。
2.3.2 静态库的使用:
gcc test_main.c -ltest_func -L ./ -o a.out //编译
gcc //使用gcc编译器编译
test_main.c //main函数
-ltest_func //-l(小写L):指定库的名字,库名砍头去尾
-L ./ //-L告诉gcc编译器从-L指定的路径(当前路径)去找静态库。默认是从/usr/lib /usr/local/lib去找
-o a.out //指定生成的最终应用程序的名字
2.4 动态库的生成和使用:
2.4.1 动态库的制作步骤:
- 使用以下指令生成动态库:
gcc -shared -fpic xxx.c -o libxxx.so
//-shared用来生成动态库
//-fpic选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码
2.4.2 动态库的使用:
gcc test_main.c -ltest_func -L ./ -o a.out //编译的语句其实和静态库相同
gcc //使用gcc编译器编译
test_main.c //main函数
-ltest_func //-l(小写L):指定库的名字,库名砍头去尾
-L ./ //-L告诉gcc编译器从-L指定的路径(当前路径)去找静态库。默认是从/usr/lib /usr/local/lib去找
-o a.out //指定生成的最终应用程序的名字
注意!虽然编译的语句相同,但是回顾动态库的缺点“需要提供依赖的动态库” ,所以编译完成后不能像使用静态库那样直接运行,这是因为动态库是程序运行中临时调用的,解决办法是将动态库拷贝到/usr/lib/下:
sudo cp libXXXX.so /usr/lib/
然后直接运行程序就可以了
将动态库复制到**/usr/lib/或/lib/下是因为程序执行时动态库的默认搜索路径就是/lib和/usr/lib**;那么如果可以指定动态库的搜索路径,就可以不需要将库复制了,这就是另一种方法:使用环境变量LD_LIBRARY_PATH指定动态库搜索路径
export LD_LIBRARY_PATH="动态库所在的绝对路径"
通过添加这个环境变量,也可以成功运行程序了,但是这样做有一个问题:*这个环境变量是临时的,也就是说只有在当前窗口生效,如果此时通过SSH再连接一个窗口,又会找不到动态库了*,解决办法是:写一个脚本start.sh:
export LD_LIBRARY_PATH="动态库所在的绝对路径" ./可执行文件
export LD_LIBRARY_PATH="/home/pi/LinuxKu/Dyku" ./a.out
然后给脚本一个可执行的权限:
chmod +x start.sh
其实这个脚本的作用就是在每次执行程序前设置一个临时的环境变量。