所向披靡のmakefile

在VS里敲代码,只需要Fn+F5就可以直接运行勒,在Linux下敲代码却要即敲命令还要用编辑器还要用编译器,那在Linux下有没有能帮我们进行自动化组建的工具呢?

当然有,超级巨星:makefile!!!!

大致认识

首先创建一个叫makefile的文件(首字母大小写都可以哦)

touch makefile

21348046d1db455c9b9ca28a2721269f.png

用vim编辑一下这个文件:

mytest:test.cgcc -o mytest test.c

这个的含义是,构建一个叫mytest的文件,这个文件依赖的是test.c 

 d30fc6c477574ed39fceaec7a2b2a0f9.png

 make一下就自动编译好啦:

2d721f6e88c644288371f747b07df086.png 继续编辑Makefile,清理生成的临时文件:

.PHONY:clean 
clean:
rm -f mytest

417b99b62d8f4719901ec50e14b9cfd5.png

这样就清理掉了:

b45d00e8297a46fd94abb6dbb421e44b.png

依赖文件列表的文件按照空格为分割:

test.c test1.c test2.c

依赖文件前必须是Tab键 

关系就是,最终的文件按照依赖文件列表用依赖方法来形成可执行程序(make会根据makefile的内容,完成编译、清理工作)

我们应该怎么理解依赖关系和依赖方法呢?

来听个小故事:墨墨酱是一名大学生,到了月末,墨墨酱摸摸自己的口袋,居然只剩十块钱勒(实在不行啃树皮吃吧,芝士就是力量),于是墨墨酱开始紧急避险,拨打电话号码,电话接通后,刚说了一声“爸”,电话就匆匆挂(墨墨酱在告知她上学需要依赖父母,虽然要钱没要成,这个过程叫表明依赖关系),但是只表明依赖关系是肯定要不到钱钱的!还要表明你的诉求:想要生活费(依赖方法)

运行原理

make是一个命令,makefile是一个文件

make的运行规则是:从上到下扫描,默认形成第一个目标文件(在默认情况下只执行一对依赖关系和依赖方法)

.PHONY后面修饰的方法总是被执行(在Linux中,如果编译的已经是最新文件,就会拒绝你的命令,因为已经是最新的了,没必要再编一遍)

e4a2a25f74804c3bbd8e356c345b18d6.png

我们希望清理总是被执行,所以上面那样写  

那么为什么makefile对最新的可执行程序,默认不想重新形成呢?

因为有很多程序运行起来肯定不止一个源文件,如果只改动了某个源文件的一小部分,就要把所有的程序重新编译,则效率很低(所以为了提高编译效率就这样做啦)

那makefile是怎么做到的呢?

为什么makefile知道我的程序被编译了呢?

因为会对比时间呀

还要从文件的属性谈起,还记得那三个时间吗?

f873e3c857034fa7a6086e1b9cba974d.png

常规开发过程:

编写源代码--->编译(对比可执行文件的最近修改时间和源文件的最近修改时间谁更新)

touch同名文件可修改对应时间,有的时间修改的规则不一样,可能重新构建一下就能正常运行了

2da578545d184ae29beefcbb13a0493a.png

在makefile里会自动进行目标文件替换,来认识一下makefile里面的内置符号:

$:取内容

@:目标文件   

^:依赖文件列表

在makefile里注释用#

0aebe2e11eb3417482866812db91c316.png

每组依赖关系匹配着对应的依赖方法 ,上面的东西写全了之后是这样的: 

code.exe:code.ogcc code.o -o code.exe
code.o:code.sgcc -c code.s -o code.o
code.s:code.igcc -S code.i -o code.s
code.i:code.cgcc -E code.c -o code.i

763c1a767fca49bc96b2c95a803e6b51.png

添加上清理的东西:

.PHONY:clean
clean:rm -f code.i code.s code.o code.exe

85287f57e11b4c4baf61762f10a0f5dc.png

自己构建的makefile就是好用:

002538e5c07840deb4bb761ba9e22fa3.png以上内容总结一下就是:makefile/make会自动根据文件中的依赖关系,进行自动推导(入栈入栈入栈,出栈出栈出栈),帮助我们执行所有相关的依赖方法 

tips:makefile支持乱序,但要把最终要形成的文件放在最前面

在makefile中支持定义变量,格式是这样的:

变量名=变量内容

 于是上面的内容改写一下:

bin=code.exe
src=code.c$(bin):$(src)gcc -o $@ $^
.PHONY:clean
clean:rm -f $(bin)

fda6ce5350db4f6695e079171c226bec.png

这个就跟宏定义差不多,在改文件的时候可以一键替换,而且以后很多地方会用到比较方便

如果不想让它打印命令就可以在前面加个@

就像这样:

bin = test.exe
src = test.c
$(bin) :$(src)@gcc -o $@ $^.PHONY:clean
clean :@rm -f $(bin)

40e5927a7e6e490ca117010fcceeaf5a.png

如果想要其打印提示信息的话就是:

bin = test.exe
src = test.c
$(bin) :$(src)@gcc -o $@ $^@echo "compiled $(src) to $(bin)..."
.PHONY:clean
clean :@rm -f $(bin)@echo "clean project"

901a82cdf19b4f5ba86cc0935a52c014.png

未知进度条 

然后来隆重介绍下Linux的进度条小程序,首先介绍两个前置知识(超绝小技巧):

回车和换行

回车和换行是什么关系呢?

我们在C中学习的\n是什么意思捏?当然是回车换行!

但是你看,(超绝老式键盘已经在提示你了)它是这样的:

3690fb6760fd46e2b52ed2f6bc715da3.jpeg

它长得像个楼梯:这代表先换行再回车呀,这是两个动作

红色的是超绝换行,蓝色的是在打字,打字之后可以拨回来,那就是超绝回车

只想回车是\r,当\r存在时(也就是\r\n的时候,\n才表示换行)

缓冲区

缓冲区是什么在哪里怎么用?

缓冲区其实就是一块内存空间

printf中的内容拷贝到缓冲区,再将相应的数据刷新到显示器上

程序要结束时一般要自动强制冲刷缓冲区,而\n也可以刷新缓冲区(行刷新),缓冲区满了也会进行刷新

请看码:

#include <stdio.h>
#include<unistd.h>
int main()
{printf("hello world!!!\n");sleep(3);return 0;
}
#include <stdio.h>
#include<unistd.h>
int main()
{printf("hello world!!!");sleep(3);return 0;
}

猜猜这两段输出都是什么? 

 7b5acd9400bb4fde9d03cb48ca6295f5.jpeg

让我来公布下正确答案:

带\n的那个肯定是先输出再睡呀

不带的那个是先睡后输出

efdba24790fa40e2a70f9823480c90db.jpeg

那这样的带不带\n的差别是什么呢,是不是函数运行的先后不同呢?又是哪个函数先运行呢?

首先明确一点:程序永远都是从上往下执行

那也就相当于执行sleep的时候早就将printf执行完了

既然printf都给我打印字符串了,那它放在哪了呢?

凭什么不让我看???

这个消失的它(bushi)是被放在了缓冲区,程序要结束时一般要自动强制冲刷缓冲区,所以在第二个例子中是先睡觉后打印(由于缓冲区没有被刷新,所以先睡,程序结束之后在显示器上显示出来)而在第一个例子中则是有\n缓冲区会自动刷新,所以是先输出再睡

至于为什么要有缓冲区这样一个设定捏?

我给你输啥你就显示啥得了呗,非要这样拖着我一下,嗯?

缓冲区的存在是为了提高效率,向外刷新的次数越少,那效率就会越高(这就跟快递小哥送快递一样,肯定是一坨一坨送快呀,总不能一颗一颗送吧)

要为外卖行业添砖JAVA啊!看我超绝锻炼车技!我要让你们都吃上超绝热乎饭!

这就设计的很绝呀,在考虑效率的同事顾及用户的需求,直接从记忆成本变成理解成本了

“老师我想回去覆盖”

“害砸你实现倒计时啦!”

#include<stdio.h>
#include<unistd.h>
int main()
{int cnt = 9;while (cnt >= 0){printf("倒计时:%d\r",cnt);fflush(stdout);cnt--;sleep(1);}return 0;
}
fflush

fflush是什么?

布吉岛,man一下:

111abd88cdce4286bbc66ba2ab882c0a.png fflush是把特定文件流中的数据刷新

int fflush(FILE *stream);

这句里面的超绝FILE*是什么意思?

很简单:Linux的设计理念:一切皆文件,那我拿显示器当文件不过分吧?

其实C程序在启动的时候会默认启动三个输入输出流,他们分别是:

ae1c00a5a7d34edcb91d8c5cc41ab7ce.png

extern FILE* stdin;           //键盘
extern FILE* stdout;          //显示器
extern FILE* stdin;           //显示器

 一般打印出的消息通过stdout打印到显示器上,那为什么程序启动默认帮我打开这三个流呢(标准输入,标准输出,标准错误),首先,打开文件是编译器和系统帮忙做的,在源代码编译时添加了一部分代码,打开键盘显示器上的代码,计算机的作用是计算,要让用户把自己的数据输入进来,经过计算后显示到显示器上让人看到结果(方便用户输入输出)

为什么要进行行刷新呢?

肯定是因为方便啊,就这样

如果这样捏?

#include<stdio.h>
#include<unistd.h>
int main()
{int cnt = 10;while (cnt >= 0){printf("倒计时:%d\r",cnt);fflush(stdout);cnt--;sleep(1);}return 0;
}

由于在显示器(字符设备)上显示的东西都是字符,所以由于超绝字符显示,就有一只超级棒的0挥之不去了,显示出来是10,90,80,70......

如果想要其正常显示,就可以这样:

#include<stdio.h>
#include<unistd.h>
int main()
{int cnt = 10;while (cnt >= 0){printf("倒计时:%2d\r",cnt);fflush(stdout);cnt--;sleep(1);}return 0;
}

 stdin                  超绝键盘

stdout,stderr    超绝显示器

同样的数据给给给,等于两份

不回显:键盘和你交互了,没把数据给显示器

超绝进度条

搞一些东西:

mkdir proccess
cd proccess
touch Processbar.c
touch Processbar.h
touch Main.c
touch makefile

e39eff9acfca4a64aff6b690045087bc.png

我们理想中的进度条是这样的:

 

能显示百分比,有进度

在实现的时候我们就用#来代表加载程度,末尾添加\0 

Processbar.h
#pragma once        //防止头文件重复展开
#include<stdio.h>
void ProcBar();
Processbar.c
#include"Processbar.h"
#include<string.h>
#include<unistd.h>
#define length 101
#define Style '#'const char* lable="|/-\\";
void ProcBar()
{char bar[length];memset(bar,'\0',sizeof(bar));int len=strlen(lable);int cnt=0;while(cnt<=100){printf("[%-100s][%3d%%][%c]\r",bar,cnt,lable[cnt%len]);fflush(stdout);bar[cnt++]=Style;usleep(20000);}printf("\n");return;
}
Main.c
#include"Processbar.h"
int main()
{ProcBar();return 0;
}
makefile
processbar:Main.c Processbar.cgcc -o $@ $^.PHONY:clean
clean:rm -f processbar

 以上就是进度条的简单实现了,但我们需要结合使用场景考虑,肯定不能是直接就单纯进度条,比如介绍一个下载的场景:

下载进度条

Processbar.h
#pragma once
#include<stdio.h>
void ProcBar(double total,double current);
Processbar.c
#include"Processbar.h"
#include<string.h>
#include<unistd.h>
#define length 101
#define Style '#'//version 2
const char* lable="|/-\\";
void ProcBar(double total,double current)
{char bar[length];memset(bar,'\0',sizeof(bar));int len=strlen(lable);int cnt=0;double rate=(current*100.0)/total;int loop_count =(int)rate;        while(cnt<=loop_count){printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lable[cnt%len]);fflush(stdout);bar[cnt++]=Style;//usleep(20000);}return;
}
Main.c
#include"Processbar.h"
#include <unistd.h>
//download
void download()
{double filesize = 100*1024*1024*1.0;	//100Mdouble current=0.0;double bandwidth = 1024*1024*1.0;	//带宽:1Mprintf("download begin,current:%lf\n",current);	while(current <= filesize){ProcBar(filesize,current);	//根据具体数据判断进度条长度//从网络中获取数据current += bandwidth;usleep(100000);   }printf("\ndownload done,filesize: %lf\n",filesize);
}
int main()
{download();//ProcBar(100.0,56.9);//ProcBar(100.0,1.0);//ProcBar(100.0,99.9);//ProcBar(100.0,100);return 0;
}
makefile
processbar:Main.c Processbar.cgcc -o $@ $^.PHONY:clean
clean:rm -f processbar

多份文件进度条

Processbar.h
#pragma once
#include<stdio.h>
typedef void(*callback_t)(double,double);
void ProcBar(double total,double current);
Processbar.c
#include"Processbar.h"
#include<string.h>
#include<unistd.h>
#define length 101
#define style '#'//version 2
const char* lable="|/-\\";
void ProcBar(double total,double current)
{char bar[length];memset(bar,'\0',sizeof(bar));int len=strlen(lable);int cnt=0;double rate=(current*100.0)/total;int loop_count =(int)rate;while(cnt<=loop_count){// printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lable[cnt%len]);// fflush(stdout);bar[cnt++]=Style;//usleep(20000);
}printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lable[cnt%len]);fflush(stdout);return;
}
Main.c
#include"Processbar.h"
#include <unistd.h>
//download
double bandwidth = 1024*1024*1.0;       //带宽:1Mvoid download(double filesize,callback_t cb)
{double current=0.0;printf("download begin,current:%lf\n",current);while(current <= filesize){cb(filesize,current);   //根据具体数据判断进度条长度//从网络中获取数据usleep(100000);current += bandwidth;}printf("\ndownload done,filesize: %lf\n",filesize);
}
int main()
{download(100*1024*1024,ProcBar);download(1*1024*1024,ProcBar);download(200*1024*1024,ProcBar);download(400*1024*1024,ProcBar);download(1000*1024*1024,ProcBar);download(10*1024*1024,ProcBar);download(50*1024*1024,ProcBar);//ProcBar(100.0,56.9);//ProcBar(100.0,1.0);//ProcBar(100.0,99.9);//ProcBar(100.0,100);return 0;
}
makefile
processbar:Main.c Processbar.cgcc -o $@ $^.PHONY:clean
clean:rm -f processbar

超绝进度条告一段落,下次见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/675617.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux进阶篇:Nginx反向代理原理与案例详解

Linux服务搭建篇&#xff1a;Nginx反向代理原理与案例详解 一、什么是正向代理 举个栗子&#xff1a; 我们在校外、公司外&#xff0c;是访问不到学校、公司的内网的&#xff0c;但是我们想要访问内网资源时&#xff0c;会用到VPN。而一般内网会存在一个VPN服务器&#xff0c…

数据结构-线性表-应用题-2.2-13

1&#xff09;使用一个用于标记的数组B[n], B的下标也就是括号里的值对应正整数&#xff0c;B[n]对应的值用来标记是否已经出现过&#xff0c;1表示出现&#xff0c;0则未出现&#xff0c;B[0]对应正整数1&#xff0c;B[n-1]对应正整数n,从A[0]开始遍历A,若能查找到第一个满足B…

STM32单片机ADC功能详解

文章目录 1. ADC概述 2. ADC结构图 3. 引脚定义 4. 转换模式 5. 数据对齐 6. 转换时间 7. 硬件电路 8. STM32使用ADC单/多通道检测数据 1. ADC概述 功能&#xff1a;ADC是一个将模拟信号&#xff08;如电压&#xff09;转换为数字信号的设备。在微控制器中&#xff0c…

C语言——联合体和枚举

1. 联合体 联合体和结构体类似。 联合体类型的声明&#xff1a; 联合体的特点&#xff1a; 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以是不同的类型。 但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀…

WIFI模块UDP电脑端调试

一&#xff0c;两端都是电脑端 1&#xff0c;电脑本机的IP地址 192.168.137.1 2&#xff0c;新建两个不同的连接&#xff0c;注意端口 二&#xff0c;WIFI 模块和电脑端连接 1&#xff0c;设置模块端目标IP和端口&#xff0c;电脑端只接收数据的话&#xff0c;IP、端口可随…

如何使用SkyWalking收集分析分布式系统的追踪数据

Apache SkyWalking 是一个开源的观测性工具&#xff0c;用于收集、分析和展示分布式系统的追踪数据。SkyWalking 支持多种语言的追踪&#xff0c;包括但不限于 Java、.NET、Node.js 等。以下是使用 SkyWalking 工具实现数据采集的详细步骤&#xff1a; 1. 下载和安装 SkyWalkin…

太原理工大学Python数据分析原理与应用(课外考题:8~11章)

这部分大概只考10分&#xff0c;且大部分出在选择题&#xff0c;填空最多一两个 (仅供参考) 第十章 (理解概念为主&#xff0c;无需看推导过程) 第十一章

Linux——mysql运维篇

回顾基本语句&#xff1a; 数据定义语言 ( DDL ) 。这类语言用于定义和修改数据库的结构&#xff0c;包括创建、删除和修改数据库、表、视图和索引等对象。主要的语句关键字包括 CREATE 、 DROP 、 ALTER 、 RENAME 、 TRUNCATE 等。 create database 数据库 &…

LINUX 入门 6

LINUX 入门 6 day10 20240505 耗时&#xff1a;41min day10 20240506 耗时&#xff1a;155min 课程链接地址 第6章 DNS协议与请求 1 DNS协议分析与项目介绍 自己去看教程 快速扫了一下&#xff0c;还是结合实践去看概念有感觉 回答以下几个问题&#xff1a; dns作用dns分层…

全国各地区劳动力流动、外出务工劳动力数、总劳动力数(2006-2021年)

01、数据简介 劳动力流动是指劳动力在不同地区、行业、职业、岗位之间的流动。它是劳动力市场运行的重要特征&#xff0c;也是实现劳动力资源优化配置的必要条件。劳动力流动可以促进劳动力资源的优化配置&#xff0c;提高劳动生产率和经济效益。据名称&#xff1a;全国各地区…

探索Baidu Comate:编程世界中的新利器

文章目录 Baidu Comate 介绍Baidu Comate的优势Baidu Comate安装过程Baidu Comate实战演练代码调优代码解释代码生成注释生成 总结 Baidu Comate 介绍 随着GPT的大火&#xff0c;衍生了各种AI工具&#xff0c;这些AI工具遍布在各行业各领域中&#xff0c;有AI写作、AI办公、AI…

时间复杂度与空间复杂度(上篇)

目录 前言时间复杂度 前言 算法在运行的过程中要消耗时间资源和空间资源 所以衡量一个算法的好坏要看空间复杂度和时间复杂度&#xff0c; 时间复杂度衡量一个算法的运行快慢 空间复杂度是一个算法运行所需要的额外的空间 一个算法中我们更关心的是时间复杂度 时间复杂度 时…