Linux库概念及相关编程(面试重点)
好处:分模块的编程思想
例如分文件编程,分3块
网络 a
超声波b
电机c
- a.功能责任划分
- b.方便调试
- c.主程序简洁
我们先来写一个简单的程序
#include
int add(int a,int b){
return a + b;
}
int chu(int a,int b) {
return a / b;
}
int jian(int a, int b) {
return a-b;
}
int cheng(int a, int b) {
return a * b;
}
int main()
{
int data1;
int data2;
int ret;
float retfloat;
printf("请输入第一个数字\n");
scanf("%d",&data1);
printf("请输入第二个数字\n");
scanf("%d",&data2);
ret =add (data1,data2);
printf("%d+%d=%d\n",data1,data2,ret);
ret =jian (data1,data2);
printf("%d-%d=%d\n",data1,data2,ret);
ret =cheng (data1,data2);
printf("%d*%d=%d\n",data1,data2,ret);
ret =chu (data1,data2);
printf("%d/%d=%d\n",data1,data2,ret);
return 0;
}
按分文件编程的话,
我们先按功能分为两个部分
功能模块(
test2.c
)
int add(int a,int b){
return a + b;
}
int chu(int a,int b) {
return a / b;
}
int jian(int a, int b) {
return a-b;
}
int cheng(int a, int b) {
return a * b;
}
主函数模块(
test.c
)
#include
int main()
{
int data1;
int data2;
int ret;
float retfloat;
printf("请输入第一个数字\n");
scanf("%d",&data1);
printf("请输入第二个数字\n");
scanf("%d",&data2);
ret =add (data1,data2);
printf("%d+%d=%d\n",data1,data2,ret);
ret =jian (data1,data2);
printf("%d-%d=%d\n",data1,data2,ret);
ret =cheng (data1,data2);
printf("%d*%d=%d\n",data1,data2,ret);
ret =chu (data1,data2);
printf("%d/%d=%d\n",data1,data2,ret);
return 0;
}
这时候我们分文件了,如果开始编译,会出现什么效果呢?
gcc test.c test2.c
我们在linux下是可以正常进行编译的。在树莓派中会提示警告 ⚠。
我们应当再把函数进行声明。
声明模块(
test.h
)
int add(int a,int b);
int chu(int a,int b) ;
int jian(int a, int b) ;
int cheng(int a, int b) ;
我们在主函数中加入我头文件 #include"test.h"
主函数模块(
test.c
)
#include
#include"demo.h"
int main()
{
......
}
这样就可以在树莓派中无警告使用分文件编译了。
库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。就是将源代码转化为二进制格式的源代码,相当于进行了加密,别人可以使用库,但是看不到库中的内容。
我们如何在Linux下创建自己的程序函数库
程序函数库可分为3种类型:
静态函数库(static libraries)
共享函数库(shared libraries)
动态加载函数库(dynamicallyloaded libraries)
1、静态函数库,是在程序执行前就加入到目标程序中去了,文件后缀是.a
2、动态函数库同共享函数库是一个东西(在linux上叫共享对象库, 文件后缀是.so
,windows上叫动态加载函数库, 文件后缀是.dll
)
静态函数库, 是在程序执行前(编译)就加入到目标程序中去了;
…
优点: 快
- 被打包到应用程序中运行速度快
- 发布程序无需提供静态库,因为已经在app中,移植方便
缺点:大
- 连接时候完整拷贝到可执行文件中,被多次使用就会有多份冗余拷贝,文件大。
- 更新,部署,发布麻烦
命名规则
静态库文件名的命名方式是“libxxx.a”,库名前加”lib”,后缀用”.a”,“xxx”为静态库名。
制作步骤
原材料:源代码.c 或者 .cpp
有两步
将.c文件生成.o,gcc a.c b.c -c
将.o 打包
ar rcs lib静态库的名字.a 原材料.o
ar rcs libtest.a b.o
gcc test2.c -c
生成 xxx.o文件:test2.oar rcs libtext.a b.o
,生成libxxx.a文件:libtext.a库的使用
给别人使用库时候,就给xxx.a 和 .h 就行。
编译gcc 主函数.c -lxxx -L ./ -o mainProStatic
-lxxx是-l
加上上面生成的libxxx
.a 的xxx
-ltext ,-l
是指定要用的动态库,库名砍头(lib)去尾(.a)
gcc test.c -ltext -L ./ -o mainProStatic
-ltext ,-l
是指定要链接的库,库名砍头(lib)去尾(.a)
-L
告诉gcc编译器从-L指定的路径去找静态库。默认是从/usr/lib 或者从/usr/local/lib去找
gcc calculatorT.c -lcalc -L ./ -o mainProDy libcalc.so
动态库:动态函数库,是在程序执行时动态(临时)由目标程序去调用
…
优点:小
- 小,连接时候不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。
- 程序升级简单,因为app里没有库的源代码,升级后只要库的名字不变,函数名以及参数不变,只是实现做了优化,就能加载成功
缺点:慢 运行慢
- 加载速度比静态库慢
- 发布程序需要提供依赖的动态库
命名规则
动态库的命名方式与静态库类似,前缀相同,为“lib
”,后缀变为“.so
”。所以为“libmytime.so”
制作步骤
将源文件生成.o,需要加一个参数fpic
打包,gcc -shared -fpic 功能代码.c -o libxxx.so
-shared 是指定生成动态库
-fpic 是一种标准(fpic 选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码)
把制作好的库放到目录lib中,不放也行,其实只要有这个库就行,在这里,移到上层目录中的lib中
库的使用
编译 gcc 主程序.c -l库名 -L ./ -o mainProDy
gcc test.c -lclac -L ./ -o mainProDy
这时候我们运行./mainProDy
,发现执行不起来 ,找不到动态库。
动态和静态的区别就体现出来了,动态是在程序执行时动态(临时)由目标程序去调用。 运行的时候会去找动态库,但是找不到动态库。静态库不用去找静态库,因为它编译进去程序里了。
这时候我们编译
sudo cp libtext.so /usr/lib/
在运行./mainProDy
就可以运行了,因为它会默认去/usr/lib/找动态库
如何指定运行的时候在当前路径下找呢?
指定运行的时候在当前路径
方法一:
通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。export
(环境变量)LD_LIBRARY_PATH=”LD_LIBRARY_PATH=“路径”
这样的方法也是只能在当前路径下使用,换目录就不行了。我们可以使用脚本来执行。
shell 脚本 vi start.sh
LD_LIBRARY_PATH="LD_LIBRARY_PATH=“/home/pi/ku"
./mainProDy
给脚本加可执行权限 chmod +x start.sh
在运行
./start.sh
脚本复习来点击这里
方法二:
在编译目标代码时指定该程序的动态库搜索路径。 还可以在编译目标代码时指定程序的动态库搜索路径。通过gcc
的参数”-Wl,-rpath,”指定