🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
目录
[2.2 伪目标](#2.2 伪目标)
[2.3 make如果知道bin和.c的新旧关系](#2.3 make如果知道bin和.c的新旧关系)
[2.4 继续手动编译过程:加强理解](#2.4 继续手动编译过程:加强理解)
[2.5 适度扩展语法:解决仍存在的多文件繁琐问题](#2.5 适度扩展语法:解决仍存在的多文件繁琐问题)
[三.实战:利用make makefile制作进度条](#三.实战:利用make makefile制作进度条)
[3.1 版本一](#3.1 版本一)
一:前言
在上一章我们说过,为了不暴露源码,我们可以先将.c文件编译好再发送给他人,但如果.c文件过多,发送和他人使用也麻烦,这章我们将解决该问题
1.1补充知识
⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀
系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄
于进⾏更复杂的功能操作
makefile带来的好处就是⸺"⾃动化编译",⼀旦写好,只需要⼀个make命令,整个⼯程完全
⾃动编译,极⼤的提⾼了软件开发的效率
make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具
makefile内部包含依赖关系和依赖方式
二:理解
我们先创建一个makefile文件(m大小写都是可以的)和myproc.c文件
myproc.c
bash#include <stdio.h> 2 int main() 3 { 4 printf("hello Milestone\n"); 5 return 0; 6 }makefile
bash1: makefile 1 myproc:myproc.c (必须是tab键一下) 2 gcc -o myproc myproc.c 3 .PhONY:clean 4 clean: (必须是tab键一下) 5 rm -f myproc 6然后我们再运行make,即可编译成功,再进行输出
bash[lcb@hcss-ecs-1cde 1]$ make gcc -o myproc myproc.c [lcb@hcss-ecs-1cde 1]$ ll total 32 drwxrwxr-x 2 lcb lcb 4096 Dec 12 22:24 lib -rw-rw-r-- 1 lcb lcb 69 Dec 12 22:06 main.c -rw-rw-r-- 1 lcb lcb 75 Dec 13 08:25 makefile -rwxrwxr-x 1 lcb lcb 8360 Dec 13 08:25 myproc -rw-rw-r-- 1 lcb lcb 77 Dec 13 08:24 myproc.c drwxrwxr-x 2 lcb lcb 4096 Dec 12 19:36 test [lcb@hcss-ecs-1cde 1]$ ./myproc hello Milestone [lcb@hcss-ecs-1cde 1]$ make clean rm -f myproc注意:项目是要被清理的,所以把clean的目标文件设置为伪目标(后续再讲)
上面的makefile的文件中
依赖关系:
上⾯的⽂件myproc,它依赖myproc.c
依赖方式
gcc -o myproc myproc.c ,就是与之对应的依赖关系
2.1引入小故事:加强理解
月底没生活费了,你打电话给家里人(你和家里人的关系就是依赖关系),问他们寻求支助生活费(这就是依赖方式)
注:依赖关系与依赖方式必须对得上才有用(你不可能打电话问室友的爸爸要生活费)
2.2 伪目标
项目工程是需要被clean的,⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤ .PHONY 修饰,伪⽬标的特性是,总是被执⾏的
即似乎myproc没有伪目标,就不会总是被执行
bash
[lcb@hcss-ecs-1cde 1]$ make
gcc -o myproc myproc.c
[lcb@hcss-ecs-1cde 1]$ make
make: `myproc' is up to date.
[lcb@hcss-ecs-1cde 1]$ make clean
rm -f myproc
[lcb@hcss-ecs-1cde 1]$ make clean
rm -f myproc
[lcb@hcss-ecs-1cde 1]$
上面也确实如此
注:编译器编译时是自上而下,也就是说如果把clean放在最前面,那make执行的就是clean,而不是myproc
同样,如果我们为myproc加上.PHONY呢,事实就是也可以无限次执行
bash[lcb@hcss-ecs-1cde 1]$ make gcc -o myproc myproc.c [lcb@hcss-ecs-1cde 1]$ make gcc -o myproc myproc.c [lcb@hcss-ecs-1cde 1]$ make gcc -o myproc myproc.c [lcb@hcss-ecs-1cde 1]$可以如此的原因是:编译器默认老代码不再编译
2.3 make如果知道bin和.c的新旧关系
make通过Modify知道bin和.c的新旧关系
此处就需要回顾前面提到的时间戳及文件组成:内容+属性
Modify: 内容变更,时间更新
Change :属性变更,时间更新
Access(特殊) :常指的是⽂件最近⼀次被访问的时间。在 Linux 的早期版本中,每当⽂件被访问时,其atime都会更新。但这种机制会导致⼤量的 IO 操作。具体更新原则,不做过多解释。
bash[lcb@hcss-ecs-1cde 1]$ stat makefile File: 'makefile' Size: 82 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 140743 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ lcb) Gid: ( 1000/ lcb) Access: 2025-12-13 08:37:17.590512411 +0800 Modify: 2025-12-13 08:37:15.249728856 +0800 Change: 2025-12-13 08:37:15.250728764 +0800 Birth: -注意,如果修改内容,属于也变更(修改内容很有可能会更新大小,大小属于属性)
下面画个图理解
左边的为旧的,右边的为新的

此时如果再进行make执行,由于bin的时间比.c文件新,所以.c为老代码,不再编译
此时如果进行修改,改变.c的时间戳

那么.c文件与myproc相比,就为新了,可进行修改,那么就可以执行文件,进行编译

2.4 继续手动编译过程:加强理解
在上一章我们已经详细讲述了编译过程,此处不再解释,只是给出代码
bash
myproc:myproc.o
gcc myproc.o -o myproc
myproc.o:myproc.s
gcc -c myproc.s -o myproc.o
myproc.s:myproc.i
gcc -S myproc.i -o myproc.s
myproc.i:myproc.c
gcc -E myproc.c -o myproc.i
.PHONY:clean
clean:
rm -f *.i *.s *.o myproc
上面识别过程由上到下压栈,要是符合,再由下到上出栈

2.5 适度扩展语法:解决仍存在的多文件繁琐问题
bash
N=proc.exe
2 CC=gcc
3 SRC=$(wildcard *.c) # 修正:*c → *.c,匹配所有.c文件
4 OBJ=$(SRC:.c=.o) # 从.c文件推导.o文件列表
5 LFLAGS=-o # 链接选项(gcc -o 目标 文件)
6 FLAGS=-c # 编译选项(gcc -c 只编译不链接)
7 RM=rm -rf # 修正:=后去掉空格,补充rm命令(原只写了参数)
8
9 # 2. 链接规则
10 $(BIN):$(OBJ)
11 @$(CC) $(LFLAGS) $@ $^ # $@=proc.exe,$^=所有.o文件
12 @echo "linking ... $^ to $@"
13
14 # 3. 编译规则(.c → .o,批量编译)
15 %.o:%.c
16 @$(CC) $(FLAGS) $< -o $@ # 补充-o $@,明确输出.o文件(原缺失会导致编译产物命名异常)
17 @echo "compiling ... $< to $@" # 修正拼写:compling
18
19 # 4. 清理伪目标(修正RM变量后可正常删除文件)
20 .PHONY:clean
21 clean:
22 $(RM) $(OBJ) $(BIN)
23
24 # 5. 测试伪目标(修正执行命令,逻辑合理化)
25 .PHONY: test
26 test: $(BIN) # 先确保可执行文件已构建,再执行测试
27 @echo "=== Source files: $(SRC) ==="
28 @echo "=== Object files: $(OBJ) ==="
29 @./$(BIN) # 修正:加./执行当前目录的可执行文件(Windows可直接写$(BIN
~
~
~
注:上面的SRC也可改为 SRC=$(shell *.c)
三.实战:利用make makefile制作进度条
3.1 版本一
makefile
bash1 SRC =$(wildcard *.c) 2 OBJ=$(SRC:.c=.o) 3 BIN=processbar 4 5 $(BIN) : $(OBJ) 6 gcc -o $@ $^ 7 %.o:%.c 8 gcc -c $< 9 .PHONY:clean 10 clean: 11 rm -rf $(OBJ) 12 rm -rf $(BIN)核心代码 main.c和process.c
bash1 #include<stdio.h> 1 SRC =$(wildcard *.c) 2 #include "process.h" | 2 OBJ=$(SRC:.c=.o) 3 int main() | 3 BIN=processbar 4 { | 4 5 double total=1024.0; | 5 $(BIN) : $(OBJ) 6 double speed=1.0; | 6 gcc -o $@ $^ 7 double current=0.0; | 7 %.o:%.c 8 while(current<=total) | 8 gcc -c $< 9 { | 9 .PHONY:clean E> 10 Flushprocess(total,current); | 10 clean: 11 current+=speed; | 11 rm -rf $(OBJ) 12 | 12 rm -rf $(BIN) 13 usleep(1000); |~ 14 } |~ 15 printf("Download %.2fMB done\n",current); |~ 16 //process_v1(); |~ 17 return 0; |~ 18 }process.c
bash2 #include"process.h" | 2 OBJ=$(SRC:.c=.o) 3 #include<string.h> | 3 BIN=processbar 4 #define NUM 101 | 4 5 #define STYLE '=' | 5 $(BIN) : $(OBJ) 6 void process_v1() | 6 gcc -o $@ $^ 7 { | 7 %.o:%.c 8 char buffer[NUM]; | 8 gcc -c $< W> 9 char*LABEL="|/-\\"; | 9 .PHONY:clean 10 memset(buffer,0,sizeof(buffer)); | 10 clean: 11 int num=0; | 11 rm -rf $(OBJ) 12 int len=strlen(LABEL); | 12 rm -rf $(BIN) 13 while(num<=100) |~ 14 { |~ 15 int cur =(num%len); |~ 16 printf("[%-100s][%d%%][%c]\r",buffer,num,LABEL[cur]); |~ 17 fflush(stdout); |~ 18 usleep(99999); |~ 19 buffer[num++]=STYLE; |~ 20 } |~ 21 printf("\n"); |~ 22 } |~ 23 void Flushprocess(double total,double current) |~ 24 { |~ 25 char buffer[NUM]; |~ 26 memset(buffer,0,sizeof(buffer)); |~ W> 27 char *lable="|/-\\"; |~ 28 int len = strlen(lable); |~ 29 static int cnt = 0; |~ 30 // 不需要⾃⼰循环,填充# |~ 31 int num = (int)(current*100/total); // 11.0 / 1000 |~ 32 int i = 0; |~ 33 for(; i < num; i++) |~ 34 { |~ 35 buffer[i] = STYLE; |~ 36 } |~ 37 double rate = current/total; |~ 38 cnt %= len; |~ 39 printf("[%-100s][%.1f%%][%c]\r", buffer, rate*100, lable[cnt]); |~ 40 cnt++; |~ 41 fflush(stdout); |~ 42 } |~ 43
但上面代码有所缺陷,即download定义的太多死板,后面更新 上传又要重新传参,因此我们可以使用前面c语言学习的知识,回调函数
3.2版本二
修改的核心代码
bash
#include<stdio.h>
2 #include "process.h"
E> 3 typedef void(*) callback(double total,double current);
4 double total =1024.0;
5 double speed =1.0;
E> 6 void Download(callback cb)
7 {
8 double current =0.0;
9 while(current<total)
10 {
11 cb(total,current)
12 usleep(3000);
13 current+=speed;
14 }
15 printf("\ndownload%.2fMB done\n",current);
16 }
17 int main()
18 {
19 Download();
20 return 0;
21 }
上面的修改即可解决