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