关于网站建设分类,外包公司要不要去,上海网站建设公司大全,软件下载app排行榜在之前的Linux系统编程中#xff0c;学习了文件的打开#xff1b;关闭#xff1b;读写#xff1b;进程#xff1b;线程等概念....
本节补充“Linux库概念 相关编程”#xff0c;这是一个面试的重点#xff01; 分文件编程
在之前的学习中#xff0c;面对较大的…在之前的Linux系统编程中学习了文件的打开关闭读写进程线程等概念....
本节补充“Linux库概念 相关编程”这是一个面试的重点 分文件编程
在之前的学习中面对较大的项目比如 STM32的小车 或 香橙派实现的智能垃圾桶 都使用了分文件编程的思路。
其 实现的核心思想就是将功能性函数的实现单独写在其他的地方在main函数中调用那些封装好的功能性函数。
这样做的好处是
分模块的编程思想在实际工作中面对大型项目可以让A完成串口开发B完成网络开发最后只需要他们提供h文件中的函数接口就可以在主函数中直接调用了测试时发现哪部分有问题可以直接找负责的人方便调试代码可移植性更强因为分文件编程了串口网络语音可能都被封装好了那么后续如果其他项目需要这些功能就可以直接调用封装好的接口了最多只需要微调main函数更加精简由于把功能性函数的实现步骤都封装到其他文件了main函数就可以专注于项目的整体调用逻辑使得整个main看起来更加清晰逻辑通畅
具体步骤
将功能性函数名和具体实现步骤写在一个.c文件中创建一个同名的.h文件包含所有可能会被调用的函数原型去除函数体在main中包含刚刚创建的.h文件在main中调用被封装好的函数接口使用gcc 编译所有相关的.c文件封装函数的.c文件main函数所在的.c文件 h文件的大概格式 int add(int x, int y);
int min(int x, int y);
float div(int x, int y); main函数调用h文件的格式 #include stdio.h
#include XXX.h Q为什么同样是调用头文件有时候使用 而有时候使用 呢? A使用 时gcc在编译时会去“/usr/include/”或“/usr/local/include/”下找这个头文件而使用 时gcc则会优先从代码运行的当前路径去找这个头文件如果找不到才会再去“/usr/include/”或“/usr/local/include/”下找这个头文件。 Linux的库
分文件编程的好处已经在刚刚说到但是实际工作中会出现这种情况程序员允许别人可以调用他封装好的功能性函数但是他不希望别人可以看到他具体实现的函数体。在这种情况下就要引入Linux的库的概念了
库程序函数库是一种可执行的二进制形式、就是将源代码转化为二进制格式相当于进行了加密别人可以使用库但是看不到库中的内容。
库是别人写好的现有的可以复用的代码现实中每个程序都要依赖很多基础的底层库不可能每个人的代码都从零开始因此库的存在意义非同寻常。
程序函数库可分为3种类型静态函数库static libraries、共享函数库shared libraries、动态加载函数库dynamically loaded libraries:
静态函数库在程序执行前就加入到目标程序中的库 文件后缀是.a 共享函数库在程序执行时动态临时由目标程序去调用共享函数库动态函数库共享对象库Linux 文件后缀是.so 动态加载函数库本质上和共享函数库是一个东西“动态加载数据库”是windows中的叫法文件后缀是.dll
因此对于Linux系统来说可以简单的将库分为 动态库 和 静态库 静态库和动态库的比较 静态数据库libXXX.a 优点 运行快发布程序无需提供静态库因为已经在app中移植方便 缺点 程序大更新部署发布麻烦 动态数据库libXXX.so 优点 程序小升级简单不同的应用程序如果调用相同的库那么在内存里只需要有一份该共享动态库的实例 缺点 运行相对慢 需要提供依赖的动态库 静态库的制作不太常用了
制作步骤
使用以下指令将.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文件此时代码执行者可以调用库但却无法得知库中函数具体的实现步骤了。 库的使用
gcc XXXXX.c -L 库文件所在目录 -lXXXX -o XXX
//-L将-L之后跟着的目录作为第一个寻找库文件的目录寻找的顺序是-L之后跟着的目录 --/lib--/usr/lib--/usr/local/lib
//-l(小写L)指定库的名字去掉lib和.a
//-o指定生成的最终应用程序的名字 小插曲gcc编译时“-I(大写i)” 和“-L的区别 -I(大写i)将-I之后跟着的目录作为第一个寻找头文件的目录寻找的顺序是-I之后跟着的目录--/usr/include--/usr/local/include-L将-L之后跟着的目录作为第一个寻找库文件的目录寻找的顺序是-L之后跟着的目录 --/lib--/usr/lib--/usr/local/lib头文件和库文件的关系库文件可以包含头文件头文件不可以包含库文件头文件可视库文件不可视 由于之前提到过静态库的优点之一是“发布程序无需提供静态库” 所以编译完成后就可以直接运行程序了不需要任何后缀 动态库的制作更常用
制作步骤
使用以下指令生成动态库
gcc -shared -fpic xxx.c -o libxxx.so
//-shared用来生成动态库
//-fpic选项作用于编译阶段在生成目标文件时就得使用该选项以生成位置无关的代码
库的使用
编译的语句其实和静态库相同
gcc XXXXX.c -L 库文件所在目录 -lXXXX -o XXX
//-L将-L之后跟着的目录作为第一个寻找库文件的目录寻找的顺序是-L之后跟着的目录 --/lib--/usr/lib--/usr/local/lib
//-l(小写L)指定库的名字去掉lib和.a
//-o指定生成的最终应用程序的名字
注意虽然编译的语句相同但是回顾动态库的缺点“需要提供依赖的动态库” 所以编译完成后不能像使用静态库那样直接运行这是因为动态库是程序运行中临时调用的解决办法是将动态库拷贝到/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动态库所在的绝对路径./可执行文件 然后给脚本一个可执行的权限 chmod x start.sh 其实这个脚本的作用就是在每次执行程序前设置一个临时的环境变量。 动态库的实操演示
首先在树莓派家目录下创建一个mjm_code文件夹学习用的代码全放在这里面 然后分别创建一个“test_main.c”和一个“test_func.c”来模拟main函数所在的C文件和封装功能函数的C文件
test_main.c
#include stdio.h
#include test_func.hint main()
{int a;int b;int ret;float ret1;printf(请输入第一个数\n);scanf(%d,a);printf(请输入第二个数\n);scanf(%d,b);printf(开始计算\n);ret add(a,b);printf(相加为%d\n,ret);ret min(a,b);printf(相减为%d\n,ret);ret mul(a,b);printf(相乘为%d\n,ret);ret1 div(a,b);printf(相除为%f\n,ret1);return 0;
}
test_func.c
int add(int a,int b)
{return ab;
}
int min(int a,int b)
{return a-b;
}
int mul(int a,int b)
{return a*b;
}
float div(int a,int b)
{return (float)a/b;
}
test_func.h
int add(int a,int b);
int min(int a,int b);
int mul(int a,int b);
float div(int a,int b);
然后尝试编译运行 没有报错程序正常运行到此为止就是一个分文件编程的典型例子。
接下来尝试将test_func.c做成一个动态库(库名我这里叫“scalc”) gcc -shared -fpic test_func.c -o libscalc.so然后进行编译我生成了一个叫“calc”的可执行程序
gcc test_main.c -L . -lscalc -o calc
//-L后的“.”代表当前路径
然后将生成的.so文件复制到usr/lib/下并删除当前路径下的.so文件
1. sudo cp libscalc.so /usr/lib/
2. rm libscalc.so //可以不删删了是为了证明复制到/usr/lib下之后当前路径的.so就没啥用了最后尝试运行 成功运行此时程序的执行只依赖.h文件和.so文件了执行者可以调用接口但并不能查看test_func.c中功能函数的具体实现因为这个.c文件已经被制作成.so的库了。