作者:@阿亮joy.
专栏:《学会Linux》
座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在实际应用中,一个 C 语言的工程项目常常由多个文件组成,此时为了对多个文件进行管理和处理,可以使用 make 项目管理器。
使用项目管理器的主要目的是用于管理较多的文件。在上一篇博客介绍过 C 语言代码的编译过程分为预处理、编译、汇编和链接,其中编译阶段仅检查语法错误以及函数与变量是否被正确地声明了,在链接阶段则主要完成函数链接和全局变量的链接。因此,那些没有改动的源代码根本不需要重新编译,只要把它们重新链接进去就可以了。这是, make 项目管理器就应运而生了。
实际上,make 项目管理器也就是个“自动编译管理器”。这里的“自动”是指它能够根据文件的时间戳自动发现更新过的文件而减少编译的工作量,同时其通过读入 makefile 文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了,所以大大提高了实际项目的工作效率。
注:make 是一个命令,而 makefile 是一个文件。
makefile 是 make 项目管理器中使用的配置文件,其通常有一下几个部分组成。
- 目标体:make 项目管理器生成的目标文件或者可执行文件。
- 依赖文件:make 项目管理器创建目标体所需要的文件,通常是 C 语言的源文件和头文件等。
- 相关操作命令:make 项目管理器使用依赖文件来创建目标体所需要的命令,这些操作命令必须以制表符(Tab)开头。
- makefile 存在的意义就是为了构建项目。
- 注:Makefile 也是可以的。
Makefile 文件样例
上图告诉我们,可执行程序 mycode 已经是最新了。为什么会这样呢?系统是如何得知的呢?为了解决这个问题,我们先来想一个问题:如果一份源代码没有任何的修改,该代码是否还需要再编译一次呢?答案是,肯定不需要。
虽然 make 指令连续使用,系统会提示你。但是 make clean 指令连续使用,系统并不会提示任何的信息。
其实这就是.PHONY
的作用所在。被.PHONY
关键字修饰的对象是伪目标,伪目标总是会被执行相关操作命令的。其实我们所要生成的目标体,也可以用.PHONY
关键字修饰,那么 make 指令就可以被连续执行了。但是通常来说,我们所要生成的目标体是不需要被.PHONY
关键字修饰的。
那么,我们现在来回答上面的问题:gcc 是如何得知我们的源代码不需要再编译了呢?这就需要涉及文件的三个时间了。
上图所示,我们多次访问 mycode.c 文件,但该文件的访问时间 Access 却没有变化,这是为什么呢?在之前,只要你访问了文件,那么文件访问时间 Access 就会改变。但是现在不会。因为在操作文件的时候,访问文件的操作比较多;如果每访问文件一次,就修改文件的访问时间 Access,那么 Access 时间将会被频繁地修改,这就需要进行更多次的 IO。而且文件的访问时间 Access 也不是特别的重要。所以新的 Linux 系统要求一段时间后访问文件,才会修改文件的访问时间。
知道了文件的三个时间,我们就能回到上面的问题了。第一次编译时,源代码的修改时间肯定是早于可执行程序的修改时间的。如果源代码的修改时间晚于可执行程序的修改时间,那么就需要重新编译源代码,否则不需要编译源代码。
touch 已经存在的文件 #将文件的时间更新为现在的时间
那么,.PHONY
关键字就是不管源文件和可执行程序的修改时间的新旧,你只要帮我重新编译就行了。
注:makefile 中的第一个目标体,使用 make 命令时可以省略目标体名称。make 命令默认从上到下执行,执行形成一个目标体,形成一个目标体后,后面的相关操作命令便不会执行了。
makefile 的依赖关系
现象:先打印字符串"you can see me..."
,然后睡了两秒。
现象:先睡两秒,再打印字符串"you can see me..."
。
关于第二个列子的现象,首先执行的一定是 printf 函数,因为我们的代码是顺序结构。先执行 printf 函数不代表数据先显示。所以该现象的解释就是已经执行 printf 函数,只不过该数据没有被立即显示出来。睡完两秒后,数据才被刷新出来。那休眠的期间,printf 函数一定执行完成打印了,那么对应输出的数据在哪里呢?答案是在缓冲区里面!
那如果我们想要 print 函数打印的内容立即显示出来,怎么才能做到呢?答:调用 fflush 函数刷新缓冲区。
那想问大家一个问题:为什么第一个例子中加了个'\n'
就能够马上打印内容呢?其实'\n'
是行缓冲,有'\n'
的话,就会把一行内容刷新出来,没有就不刷新。
现象:先打印字符串"you can see me..."
,然后睡了两秒。
- 回车
\r
是回到当前行的最开始- 换行
\n
是换到下一行- 一般而言,
\r\n
是回车换行。但在语言层面上,\n
就是回车换行
补充知识:显示器为什么能显示各种符号?显示器面板上有各种像素点,那么点亮显示器上对应的像素点就能显示各种符号了。凡是显示到显示器上的东西,都是字符。
倒计时程序
#include
#include
int main()
{
int cnt = 10;
while(cnt)
{
printf("剩余时间:%2d\r",cnt--); // %2d一次刷新两个字符
fflush(stdout); // 刷新缓冲区
sleep(1); // 休眠一秒钟
}
return 0;
}
进度条程序
注:头文件不用在 makefile 里写,编译器能够在当前目录下找到。
// makefile文件里的内容,注:-D N=1为命令行参数
ProcessOn:process.c main.c
gcc process.c main.c -o ProcessOn -D N=1
.PHONY:clean
clean:
rm -f ProcessOn
// process.h
#pragma once
#include
#include
#include
#define NUM 101
#define S_NUM 5
extern void ProncessOn(); //函数的声明
// process.c
#include "process.h"
void ProncessOn() //函数定义
{
int count = 0;
char bar[NUM];
memset(bar, '\0', sizeof(bar));
//reserve
const char* lable = "|/-\\";
// 进度条样式
char style[S_NUM] = {'+', '#', '>', '-', '.'};
while(count <= 100)
{
printf("\033[42;34m[%-100s][%d%%][%c]\033[0m\r", bar, count, lable[count%4]); //%-s向左对齐
fflush(stdout);
bar[count++] = style[N];
//sleep(1); //sleep参数的单位为秒,休眠时间太长
usleep(50000); //usleep参数的单位为微妙
}
printf("\n");
}
// main.c
#include "process.h"
int main()
{
ProncessOn(); //函数调用
return 0;
}
颜色控制格式
本篇博客主要讲解 make、makefile 以及 Linux 下的第一个小程序进度条。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!❣️