学习笔记 day9 C语言:makefile与库

1 使用makefile管理库文件实现项目编译

使用静态库:
TARGET:=run
OBJS:=main.o func.o func2.o libfun.a
BINPATH:=./main/main.o ./func/func.o ./func2/func2.o
CC:=gcc
CFLAGS:= -c -o
CFLAGSs:= -o
$(TARGET):$(OBJS)
	$(CC) main/main.c -o run -L ./lib  -I ./include/ -lfun
VPATH=./main ./func/ ./func2/ 

libfun.a:func.o func2.o
	ar rcs ./lib/libfun.a ./func/func.o ./func2/func2.o

%.o:%.c
	make -C $*
.PHONY:clean
clean:
	rm -rf ./lib/libfun.a
	rm -rf $(BINPATH) $(TARGET)
使用动态库:
OBJS:= libfun.so
CC:= gcc
action:= -shared -fPIC
so:= lib/libfun.so
main:= main/main.c
fun:= func/func.c func2/func2.c
inc:= -I ./include/

$(TARGET):$(OBJS)
	$(CC) $(main) $(so) $(inc) -o $(TARGET)
$(OBJS):
	$(CC) $(action) $(fun) $(inc) -o $(so)

.PHONY:clean
clean:
	rm -rf run
	rm -rf lib/libfun.so

2 判断一个字符串是否是合法的IP地址?(192.168.98.123)
(点分十进制,(1~254).(0-255).(0-255).(0-254))

#include<stdio.h>
#include<string.h>
int isLegalIp(char s[]){
	int a[4]={0};
	int count=0;
	int j=0;
	char *p=s;
	while(*p){
		if(*p=='.'){
			count++;
			p++;
			continue;
		}
		if(*p<'0'||*p>'9'){
			return 0;
		}
		p++;
	}
	//printf("count:%d\n",count);
	if(count!=3){
		return 0;
	}
	p=s;
	while(*p){
		if(*p=='.'){
			p++;j++;
		}
		a[j]=a[j]*10+(*p-'0');
		
		p++;
	}
	if((a[0]<1||a[0]>254)||(a[1]<0||a[1]>255)||(a[2]<0||a[2]>255)||(a[3]<0||a[3]>254)){
		return 0;
	}
	return 1;
}

int main(int argc, const char *argv[])
{
	char s[32];
	printf("请输入一个ip地址:\n");	

	gets(s);
	if(isLegalIp(s)==1){
		printf("该ip地址合法\n");
	}
	else{
		printf("该ip地址不合法\n");
	}
	return 0;
}

3 计算一个字符串中最大的子串的长度,并将它返回。(子串是这样定义的连续的数字或者字母)如“abcdefg123456789asdfgagfafg”,其中最大子串是“asdfgagfafg”。

#include<stdio.h>
#include<string.h>
char s2[64]={0};
int countLength(char a[]){
	char *p=a;
	int max=0,count=0;	
	int i=0,j,k;
	while(a[i]){
		count=0;
		if(a[i]>='0'&&a[i]<='9'){
			for(j=i;a[j]>='0'&&a[j]<='9';j++){
				count++;
			}
		}
		if((a[i]>='a'&&a[i]<='z')||(a[i]>='A'&&a[i]<='Z')){
			for(j=i;(a[j]>='a'&&a[j]<='z')||(a[j]>='A'&&a[j]<='Z');j++){
				count++;
			}
		}
		if(count>max){
			max=count;
			for(k=0;k<max;k++){
				s2[k]=a[i+k];
			}
			s2[k]='\0';
		}
		i++;//i=j
	}
	return max;
}
int main(int argc, const char *argv[])
{
	char s1[64]="";
	printf("请输入一个字符串:\n");
	gets(s1);
	puts(s1);
	printf("最长的字串为:%s,长度为:%d\n",s2,countLength(s1));
	return 0;
}

4

	int a				1.一个整型数;
	int *a				2.一个指向整型数的指针;
	int **a				3.一个指向指针的指针,它指向的指针是指向一个整型数;
	int a[10]			4.一个有十个整型数的数组;
	int *a[10]			5.一个有十个指针的数组,该指针是指向一个整型数的;
	int (*a)[10]		6.一个指向有十个整型数数组的指针;
	int (*a)(int b)		7.一个指向函数的指针,该函数有一个整型数参数并返回一个整数;
	int (*a[10])(int b)	8.一个有十个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数

1 静态库与动态库

静态库:在Linux中用 .a文件表示,如libfun.a。当我们使用静态库的时候,库里面的函数和变量是在编译的时候加载到可执行文件中。
动态库:在Linux中用 .so文件表示,如libfun.so。当我们使用动态可以的时候,库里面的函数和变量是在运行的时候加载到可执行文件中。

1.可执行文件的大小:静态库 > 动态库   (内粗的使用效率)
2.可执行的文件的执行速度:静态库 > 动态库
3.可执行文件的功能升级难易程度:静态库 > 动态库
4.可执行文件的代码部署: 静态库 > 动态库(代码位置部署)
5.库文件的存放路径  /lib   /usr/lib

区别:
	1 在目标文件链接成可执行文件阶段,库函数(库函数本身有一个代码段)链接进可执行文件(代码段)中,占了很大的内存空间。而使用动态库时,只是在链接时做了一个标记,当可执行程序运行时才会加载这段标记(从库路径中加载动态链接库.so文件),这样就节省了可执行程序的空间,只有在运行这段很短的时间会占用可执行程序的空间。
	2 使用动态库对库的依赖性太强,一般发布的话需要库文件(库文件要放在相应的库路径中)也发布。静态链接库对库的依赖性不会有那么强。实际上使用动态库在运行的时候加载printf也会占用可执行程序,在运行时占用可执行程序的空间其实是跟静态库是一样的。

2 静态库的制作

gcc -c func.c  --->生成func.o
gcc -c func2.c --->生成func2.o
ar rcs libfun.a func.o func2.o   --->由func.o和func2.o生成libfun.a

gcc main.c -o run -L ./../lib/ -I ./../include/ -lfun
-I:后面跟头文件的路径,注意不需要写具体的文件,精确到头文件所在的目录即可
-L:后面跟 库文件的路径,注意不需要写具体的文件,精确到库文件所在的目录即可
-l: 后面跟库文件的名字,表示链接到第三方库

3 动态库的制作

gcc -shared -fPIC func.c func2.c -I ./include/ -o libfun.so    -->生成动态库libfun.so

gcc main.c ./../libfun.so -I ./../include/ -o run   --->链接动态库和头文件的路径生成可执行文件run

4 makefile

Makefile是一个工程管理的工具。它的本质是一个文件,这个文件中存放的是对代码的编译的规则。Makefile就会根据文件的"时间戳"来决定工程内的文件本次是否需要参与编译。

5 make

make是一个可执行程序,在/usr/bin目录下存放着如果在/usr/bin目录下找不到这个程序,需要使用sudo apt-get install make来安装这个程序。当在终端上执行make的时候,它就会解析当前目录下的Makefile文件。并根据Makefile里的编译规则来编译当前的工程。

6 Make工程管理器编译Test程序的过程

(1) Make工程管理器首先会在当前目录下读取Makefile文件
(2) 查找Makefile文件中的第一个目标文件(在本例中为test),该文件也是Make工程管理器本次编译任务的最终目标。
(3) 把目标文件test的依赖文件当作目标文件进行依赖规则检查。这是一个递归的检查过程,在本例中就是依次把a.o和b.o作为目标文件来检查各自的依赖规则。Make会根据以下三种情况进行处理。
	① 如果当前目录下没有或缺少依赖文件,则执行其规则命令生成依赖文件(假如缺少a.o,则执行命令”cc  -c a.c“生成a.o)。
	② 如果存在依赖文件,则把其作为目标文件来检查依赖规则(假如 a.c比a.o新,则执行命令”cc -c a.c“更新a.o)
	③ 如果目标文件比所有依赖文件都新,则不做处理。
(4) 递归执行第三步之后,就会得到目标文件test所有最新的依赖文件了,接着Make会根据以下三种情况进行处理:
	① 如果目标文件test不存在(比如第一次编译),则执行规则命令生成test
	② 如果目标文件test存在,但存在比test要新的依赖文件,则执行规则命令更新test。
	③ 目标文件test存在,且比所有依赖文件新,则不做处理。

7 makefile中的文件查找

VPATH=目录 :目录  例如:VPATH=/a:/b
	make会在当前的路径找不到文件时按照顺序依次查找/a和/b目录
vpath 模式 目录:目录   例如:vpath %.c /a:/b
	和VPATH不同的是,vpath并不是变量,而是关键字,其作用和VPATH类似,但使用方式更加灵活。Make会在当前路径找不到文件时,按照顺序依次查找/a和/b目录中所有的C文件。vpath也可以对不同路径采用不同的搜索模式。例如:
	vpath %.c /a
	vpath %.h /b
	Make会在当前路径找不到源文件时先查找/a目录下的C文件,然后查找/b目录下的头文件.

8 makefile中的通配符

1、*  匹配0个或者是任意个字符
2、? 匹配任意一个字符
3、[ ]  指定匹配的字符放在 "[]" 中

还有一个和通配符 "*" 相类似的字符,这个字符是 "%",也是匹配任意个字符。"%.o" 把我们需要的所有的 ".o" 文件组合成为一个列表,从列表中挨个取出的每一个文件,"%" 表示取出来文件的文件名(不包含后缀),然后找到文件中和 "%"名称相同的 ".c" 文件,然后执行下面的命令,直到列表中的文件全部被取出来为止。
这个属于 Makefile 中静态模规则:规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。跟我们的多规则目标的意思相近,但是又不相同。

9 make的参数

“-b” “-m” 这两个参数的作用是忽略和其它版本 make 的兼容性。
“-B” “--always-make”认为所有的目标都需要更新(重编译)。 

“-C <dir>” “--directory=<dir>” 
指定读取 makefile 的目录。如果有多个“-C”参数,make 的解释是后面的路径以前面
的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”
等价于“make –C ~hchen/test/prog”。

“—debug[=<options>]” 
输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是<options>的取值:
a——也就是all,输出所有的调试信息。(会非常的多)
b——也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
v——也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
i——也就是implicit,输出所以的隐含规则。
j——也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
m——也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

“-f=<file>” “--file=<file>” “--makefile=<file>” 指定需要执行的 makefile。
“-h” “--help” 显示帮助信息。
“-i” “--ignore-errors” 在执行时忽略所有的错误。

“-I <dir>” “--include-dir=<dir>” 
指定一个被包含 makefile 的搜索目标。可以使用多个“-I”参数来指定多个目录。

“-k” “--keep-going” 
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。

“-l <load>” “--load-average[=<load]” “—max-load[=<load>]” 指定 make 运行命令的负载。

“-n” “--just-print” “--dry-run” “--recon” 仅输出执行过程中的命令序列,但并不执行。

10 自动化变量

$@:表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。

$%:仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。

$<:依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$?:所有比目标新的依赖目标的集合。以空格分隔。

$^:所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。

$+:这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。

$*:这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNUmake的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。

一 静态库和动态库

1.1 基本概念

静态库: windows:xxx.lib   linux:libxx.a    当我们使用静态库的时候,库里面的函数和变量是在编译的时候加载到可执行文件中。
动态库: windows: xxx.dll  linux:libxxx.so  当我们使用动态可以的时候,库里面的函数和变量是在运行的时候加载到可执行文件中。

1.2 区别

1.可执行文件的大小:静态库 > 动态库   (内粗的使用效率)
2.可执行的文件的执行速度:静态库 > 动态库
3.可执行文件的功能升级难易程度:静态库 > 动态库
4.可执行文件的代码部署: 静态库 > 动态库(代码位置部署)
5.库文件的存放路径  /lib   /usr/lib

1.3 静态库的制作

gcc -c func.c  --->生成func.o
gcc -c func2.c --->生成func2.o
ar rcs libfun.a func.o func2.o   --->由func.o和func2.o生成libfun.a

gcc main.c -o run -L ./../lib/ -I ./../include/ -lfun
-I:后面跟头文件的路径,注意不需要写具体的文件,精确到头文件所在的目录即可
-L:后面跟 库文件的路径,注意不需要写具体的文件,精确到库文件所在的目录即可
-l: 后面跟库文件的名字,表示链接到第三方库

1.4 动态库的制作

gcc -shared -fPIC func.c func2.c -I ./include/ -o libfun.so    --》生成动态库libfun.so

gcc main.c ./../libfun.so -I ./../include/ -o run   --->链接动态库和头文件的路径生成可执行文件run
-c
-o
-E
-S
-wall
-L
-I
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛奶奥利奥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值