1. 分文件编程:
在玩stc51单片机、stm32的时候为了主程序的代码简洁,我们经常会使用分文件编程的方法。在Linux C中同样可以使用分文件编程的方法让程序的逻辑更加清晰。
这是一个简单的多函数调用的实例:
#include <stdio.h>
void input2nums(int *x, int *y)
{
printf("please in put 2 int-numbers\n");
scanf("%d %d",x, y);
}
int plus(int x, int y)
{
return (x+y);
}
int minus(int x, int y)
{
return (x-y);
}
int multipy(int x, int y)
{
return (x*y);
}
float divide(int x, int y)
{
return ((float)x/y);
}
int main()
{
int a;
int b;
input2nums(&a, &b);
int sum = plus(a, b);
int diff = minus(a, b);
int mul = multipy(a, b);
float div = divide(a, b);
printf("sum: %6d\n", sum);
printf("diff: %6d\n", diff);
printf("mul: %6d\n", mul);
printf("div: %6.4f\n", div);
return 0;
}
看着比较长,我们可以把它分解成两个文件使代码看起来更直观。
我们把它分成:calcu.c, func.c和func.h
calcu.c:
#include <stdio.h>
#include "func.h"
int main()
{
int a;
int b;
input2nums(&a, &b);
int sum = plus(a, b);
int diff = minus(a, b);
int mul = multipy(a, b);
float div = divide(a, b);
printf("sum: %6d\n", sum);
printf("diff: %6d\n", diff);
printf("mul: %6d\n", mul);
printf("div: %6.4f\n", div);
return 0;
}
func.c:
#include <stdio.h>
void input2nums(int *x, int *y)
{
printf("please in put 2 int-numbers\n");
scanf("%d %d",x, y);
}
int plus(int x, int y)
{
return (x+y);
}
int minus(int x, int y)
{
return (x-y);
}
int multipy(int x, int y)
{
return (x*y);
}
float divide(int x, int y)
{
return ((float)x/y);
}
func.h:
#ifndef __FUNC_H
#define __FUNC_H
void input2nums(int *x, int *y);
int plus(int x, int y);
int minus(int x, int y);
int multipy(int x, int y);
float divide(int x, int y);
#endif
在calcu.c中对于头文件的引用使用了两种方法一种是#include <xxx.h>和include "xxx.h"。
这两种引用方法的不同点在于头文件的默认索引地点不同。<>是默认在环境变量中找,“”是默认在当前工作文件夹中寻找。我们也可以简单理解成:<官方的头文件.h>和"自己编写的头文件.h"。
编译和运行:
2. 库
程序函数库可以使整个程序更加模块化,更容易重新编译,更方便升级。而且可以在一定情况下起到代码保护的功能。
库分为:静态函数库、共享函数库、动态加载函数库。
静态库:
在程序执行前(编译)就加入到目标程序中去了。
优点:运行快,发布程序无需提供静态库,移植方便
缺点:程序大,链接时完整拷贝到可执行文件中,被多次使用就有多份冗余拷贝。更新、部署、发布麻烦。
动态库:
在程序执行时动态(临时)由目标程序去调用。
动态函数库和共享函数库是一个东西(在linux是叫做共享对象库,后缀是.so,在windows上叫做动态加载函数库,后缀是.dll)
优点:1. 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。
2.程序升级简单,因为app里面没有库的源码,升级之后只要库的名字不变,函数名以及参数不变,只是算法做了优化,就能加载成功。
缺点:运行慢,发布程序依赖于动态库。
3.库的制作
3.1静态库的制作
第一步,生成.o文件
gcc func.c -c
第二步,生成.a文件
ar rcs liblfunc.a func.o
这里liblfunc.a就是库文件(命名规则和下面的动态库基本一致)
3.2动态库的制作
命名规则:libname.so.0.0.1
lib是固定前缀 name是库名 .so是后缀 0.0.1是版本号(版本号可无)
gcc -shared -fpic func.c -o liblfunc.so
-shared #指定生成动态库
-fpic #选项作用于编译阶段,在生成目标文件时使用该选项,以生成位置无关码
4.库的使用
4.1静态库的使用
静态库的使用需要一个主程序(.c文件),一个库的头文件(.h),一个库文件(.a)。
我这里是calcu.c、func.c、liblfunc.a三个文件。
gcc calcu.c -llfunc -L ./ -o proj_static
-l #是链接库 默认路径/usr/local/lib
usage:-llfunc #这里链接到一个名为liblfunc.a的库文件
-L #是指定路径
./ #是当前路径
-o #是命名
4.2动态库的使用
使用方法和静态库相同。
gcc calcu.c -llfunc -L ./ -o proj_dynamic
生成proj_dynamic后发现不能运行,报错提示就是说明,使用动态库编译后运行也要链接动态库。
解决办法:
1. 将生成的动态库文件拷到/usr/lib/下
sudo cp liblfunc.so /usr/lib/
2. 应用动态库指定动态库的位置
export LD_LIBRARY_PATH="/home/pi/ccode/workshop"
但是这个办法是临时的,更换终端之后就失效了。
可以写一个shell脚本,将配置环境变量和运行程序一起执行就可以了。
vi start_proj_dynamic.sh
export LD_LIBRARY_PATH="/home/pi/ccode/workshop"
./proj_dynamic
chmod +x start_proj_dynamic.sh