Linux下静态库与动态库制作及分文件编程
文章目录
- Linux下静态库与动态库制作及分文件编程
- 1.分文件编程
- 1.1优点
- 1.2操作逻辑
- 1.3示例
- 2.Linux库的概念
- 3.静态库的制作与使用
- 3.1优缺点
- 3.2命名规则
- 3.3制作步骤
- 3.4开始享用
- 4.动态库的制作与使用
- 4.1优缺点
- 4.2动态库命名规则
- 4.3制作步骤
- 4.4开始享用
1.分文件编程
这部分玩过单片机的都有所了解,在.c
文件很多的时候,需要分文件编程,也叫模块化编程。
1.1优点
● 分模块的编程思想;
● 便于功能责任划分;
● 方便后期的维护和调试;
● 主程序更加简洁。
1.2操作逻辑
一篇代码基本可以分为三个部分,头文件部分,功能区部分,主函数main部分。那么分文件编程就是将这三个模块分别生成单独的代码文件。
● 功能区.c :只写功能实现的代码部分,可以不写头文件
● 主函数.c :写主函数main()的部分 ,和自己生成的“头文件.h”,用双引号“”,不用<>。
● 头文件.h :代码需要的所有头文件首先要写进去,然后功能区定义的函数及变量要写入(只写函数定义的区域,末尾加分号)。作用在于连接功能区代码和主函数代码。
1.3示例
输入两个数,分别计算出加减乘除后的值。
源代码示例(没用使用分文件编程的方式)
代码拆分:采用分文件编程,将头文件区,功能代码区,主函数区分别建立代码文件
demo.h —— 头文件
#ifndef __DEMO_H //加入预编译,防止重复加入头文件,命令方式以大写的头文件为名
#define __DEMO_H #include <stdio.h>int add(int x,int y);//加法
int sub(int x,int y);//减法
int mul(int x,int y);//乘法
float div(int x,int y);//除法#endif
demo.c —— 主函数
#include "demo.h"//自建头文件,用双引号“”,里面包含功能区函数的定义及参数
//连接主函数和功能区代码
int main()
{int data1;int data2;int value;printf("input a num:\n");scanf("%d",&data1);printf("input a num:\n");scanf("%d",&data2);printf("=============\n");printf("%d+%d=%d\n",data1,data2,add(data1,data2));//加printf("%d-%d=%d\n",data1,data2,sub(data1,data2));//减printf("%dx%d=%d\n",data1,data2,mul(data1,data2));//乘printf("%d/%d=%f\n",data1,data2,(float)div(data1,data2));//除return 0;
}
demo1.c —— 功能代码区
#include "demo.h" //一般都会加自己的头文件,因为有些宏定义都会放到这个.h里面int add(int x,int y)//加法
{return x+y;
}
int sub(int x,int y)//减法
{return x-y;
}
int mul(int x,int y)//乘法
{return x*y;
}
float div(int x,int y)//除法
{return (float)x/y;
}
编译
//1.最笨的
gcc demo.c main.c
//2.利用*通配符,编译所有以.c为结尾的文件
gcc *.c
2.Linux库的概念
一个“程序函数库”简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使用。程序函数库可以使整个程序更加模块化,更容易重新编译,而且更方便升级。
程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries): (静态库,动态库,共享库)
1、静态函数库,是在程序执行前就加入到目标程序中去了 ;
通俗点说明:你可以使用我的代码,但是你不能看我的代码;
编译时,会链接到我们生成的库文件,程序可以正常编译运行,但如果常规使用vi打开库文件,就是一页乱码。
就是把上面的.c文件封装起来融合到主文件了,只有一个.h文件供你调用。
2、动态函数库同共享函数库是一个东西(在linux上叫共享对象库, 文件后缀是.so ,windows上叫动态加载函数库, 文件后缀是*.dll)
假如只有一个文件,那么静态库就很方便,但是如果有一百个文件,都用到了这个库文件,都融合到文件里面就会使得文件占得空间巨大,而且如果这个库文件更新迭代后,所有文件都要重新编译一下,如果是使用的动态库或者说共享库的话,这一百个文件共用这一个库文件,那么会省下很大一部分空间,在更新升级迭代后只需更新这一个库文件即可。
Linux中命名系统中共享库的规则
3.静态库的制作与使用
目前不是那么常用了,不如动态库的种种,但是还是要学一下
静态函数库:是在程序执行前(编译)就加入到目标程序中去了 ;一般将功能性代码生成库文件
3.1优缺点
优点:
● 静态库在编译的时候就被打包到应用程序中,所以其加载的速度快;
● 发布程序的时候无需提供静态库,因为已经在app中,移植方便;
缺点:
● 链接时完整的拷贝到可执行文件中,被多次使用就会有多份冗余拷贝;
● 更新,部署,发布麻烦;
3.2命名规则
静态库文件的命名方式:“libxxx.a
”,库名前加“lib”,后缀用“.a”,“xxx”为静态库名
动态库以.so为后缀
3.3制作步骤
原材料:xxx.c或者.cpp文件
- 制作成.o文件(-c在前在后无所谓)
gcc -c xxx.c
- 生成静态库文件
ar rcs libtest.a xxx.o //单个
ar rcs libtest.a xxx.o xxx.o //多个
生成的静态库文件通过主函数的链接后,正常编译运行,但并不能打开库文件查看里面的内容,也是库文件制作的目的所在
其中:
ar rcs为固定命令,lib为固定开头,.a为固定结尾,中间的名字自己起
ar 命令里的内容 :
3.4开始享用
你只知道他的头文件里面的函数有什么,怎么用,但是不知道这个函数具体是怎么实现的
1,mv xxx.c ~ 将原先的功能文件和生成的.o文件移至工作目录,只留下.h头文件和主函数文件,还有生成的静态库文件。
2,gcc xxx.c -ltest -L./ 编译
- -l (L小写):链接生成的静态库文件,libtest.a需要“砍头去尾” test;
- -L: 告诉gcc编译器从-L指定的路径去找静态库(当前路径)。默认是从
/usr/lib
或/usr/local/lib
中去找
orangepi@orangepizero2:~/file$ ls
demo1.c demo1.o demo.c demo.h libtest.a
orangepi@orangepizero2:~/file$ mv demo1.c demo1.o ~ //把多余的文件移动到工作目录
orangepi@orangepizero2:~/file$ ls //只剩下库文件,头文件,主函数文件
demo.c demo.h libtest.a
orangepi@orangepizero2:~/file$ gcc demo.c -ltest -L./ //编译要链接库和路径
orangepi@orangepizero2:~/file$ ls //编译成功,生成可执行程序a.out
a.out demo.c demo.h libtest.a
orangepi@orangepizero2:~/file$ ./a.out //运行成功
4.动态库的制作与使用
动态函数库:是在程序执行时动态(临时)由目标程序去调用
4.1优缺点
优点:
- 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存;
- 程序升级简单,因为app中没有库的源代码,升级之后只要库的名字不变,函数名与及参数不变,只是做了优化,就能加载成功;
缺点:
- 加载速度比静态库慢;
- 发布程序时需要提供依赖的动态库;
4.2动态库命名规则
**动态库的命名方式:**与静态库类似,“libxxx.so”, 库名前加“lib”,后缀用“.so”,“xxx”为动态库名
4.3制作步骤
原材料:xxx.c 文件
- 将源文件生成.o,需要加一个参数fpic
gcc a.c b.c -c -fpic(fPIC)
- 生成.so文件:
gcc -shared xxx.o -o libtest.so
//一步到位的操作,不再生成.o文件,直接到.so
gcc -shared -fpic xxx.c -o libtest.sogcc -shared -fpic xxx.c -o libtest.soarm-linux-gnueabihf-gcc -shared -fpic *.c -o libtest.so
● -shared:必须使用的关键字,指定生成动态库;
● -fpic:执行标准,作用于编译阶段,在生成目标文件时就得使用该选项;
4.4开始享用
gcc xxx.c -ltest -L./
编译后生成默认a.out可执行程序,也可以自行-o生成执行程序
里面这些符号的含义,在静态库已做介绍,不再赘述
编译没问题,但是结果出了问题。动态库运行和静态库的运行方式有区别的,静态库直接生在在可执行的程序中,而动态库是在程序执行时动态(临时)由目标程序去调用,需要去找到执行的文件目录,所以上面的动态库执行出错。
动态库默认不会去当前目录下找,而是去环境变量中找。
解决方法:把生成的动态库文件libtest.so拷贝到/usr/lib/这个路径下即可
为了让系统能找到要加载的共享库,有三种方法 :1.把库拷贝到/usr/lib和/lib目录下
2.在LD_LIBRARY_PATH环境变量中添加库所在路径,,export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:你的动态库目录
3.添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新
sudo cp libtest.so /usr/lib/