所向披靡のmakefile

news2025/1/11 20:46:49

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

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

大致认识

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

touch makefile

21348046d1db455c9b9ca28a2721269f.png

用vim编辑一下这个文件:

mytest:test.c
   gcc -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.o
   gcc code.o -o code.exe
code.o:code.s
   gcc -c code.s -o code.o
code.s:code.i
   gcc -S code.i -o code.s
code.i:code.c
   gcc -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.c
	gcc -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;	//100M
	double current=0.0;
	double bandwidth = 1024*1024*1.0;	//带宽:1M
	
	printf("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.c
	gcc -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;       //带宽:1M

void 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.c
        gcc -o $@ $^

.PHONY:clean
clean:
        rm -f processbar

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

obs64无法定位程序输入点IsWow64Process2

obs安装后&#xff0c;打开提示&#xff1a;obs64无法定位程序输入点IsWow64Process2。 解决办法&#xff0c;找到obs.dll文件&#xff0c;并找软件打开。 &#xff08;我用的是 notepad打开的&#xff09; 用CTRLF 搜索 “IsWow64Process2” 对应的"32"改为"…

【容器】Pod 生命周期

概述 Pod的生命周期包含从Pod创建事件的触发到Pod被停止的整个流程。了解Pod的生命周期方便日常排障&#xff0c;并能帮助较深入了解K8s。 在Pod生命周期中有两个重要的标识&#xff1a;Pod Condition 和 Pod Phase。Pod Phase提供了一个Pod当前状况的概览&#xff0c;可以帮…

APP 在华为应用市场上架 保姆级别详细流程

1、作为一名干开发的程序员&#xff0c;第一次能把自己的APP 上架&#xff0c;对自己来说是多么有意义的一项成就 2、创建一个 华为的开发者账号 根据提示填写完注册的信息https://developer.huawei.com/consumer/cn/product/华为开发者产品 | 开发者平台 | 流量变现 | 华为开…

Three.js的几何形状

在创建物体的时候&#xff0c;需要传入两个参数&#xff0c;一个是几何形状【Geometry】&#xff0c;一个是材质【Material】 几何形状主要是存储一个物体的顶点信息&#xff0c;在Three中可以通过指定一些特征来创建几何形状&#xff0c;比如使用半径来创建一个球体。 立方体…

Android Studio查看xml文件的修改时间和记录

Android Studio查看xml文件的修改时间和记录 Android Studio里面如果是Java/Kotlin编写界面&#xff0c;可以点击函数开头上面的提交在直接&#xff0c;然后在编辑界面的左侧查看历史时间上的修改记录&#xff0c;但是xml文件里面没有直观的这样操作方式。 但xml里面可以通过快…

FileLink跨网文件交换,推动企业高效协作|半导体行业解决方案

随着信息技术的迅猛发展&#xff0c;全球信息产业已经迎来了前所未有的繁荣与变革。在这场科技革命中&#xff0c;半导体作为信息产业的基础与核心&#xff0c;其重要性日益凸显&#xff0c;半导体的应用场景和市场需求将进一步扩大。 然而&#xff0c;在这一繁荣的背后&#x…

微信公众号营销攻略,2024年微信引流商业最佳实践

确实&#xff0c;微信是中国市场上不可或缺的营销工具。下面是一些关于如何在微信上进行有效营销的最佳实践&#xff0c;以及如何通过微信公众号进行广告宣传&#xff0c;以提升品牌知名度并推动业务增长。 拥有一个微信公众号是进行微信营销的关键第一步。 通过公众号&#x…

UE5自动生成地形一:地形制作

UE5自动生成地形一&#xff1a;地形制作 常规地形制作地形编辑器地形管理添加植被手动修改部分地形的植被 置换贴图全局一致纹理制作地貌裸露岩石地形实例 常规地形制作 地形制作入门 地形导入部分 选择模式&#xff1a;地形模式。选择地形子菜单&#xff1a;管理->导入 …

吴恩达深度学习笔记:深度学习的 实践层面 (Practical aspects of Deep Learning)1.13-1.14

目录 第二门课: 改善深层神经网络&#xff1a;超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第一周&#xff1a;深度学习的 实践层面 (Practical aspects of Deep Learning)1.13 梯度检验&#…

蓝桥杯单片机之模块代码《AT24C02》

过往历程 历程1&#xff1a;秒表 历程2&#xff1a;按键显示时钟 历程3&#xff1a;列矩阵按键显示时钟 历程4&#xff1a;行矩阵按键显示时钟 历程5&#xff1a;新DS1302 历程6&#xff1a;小数点精确后两位ds18b20 历程7&#xff1a;35定时器测量频率 文章目录 过往历…

微信小程序(Taro)获取经纬度并转化为具体城市

1、获取经纬度 申请权限&#xff0c;想要使用微信小程序获取经纬度的方法是要申请该方面的权限。 获取经纬度的方法有很多选择其中一个使用就好。 我使用的是Taro.getFuzzyLocation(&#xff09; 在app.config.js中需要添加设置 requiredPrivateInfos: ["getFuzzyLocat…

安装numpy遇到的问题

安装numpy的时候提示无法安装如下&#xff1a; (venv) E:\works\AI\venv\Scripts>pip install numpy pandas matplotlib jupyter -i https://pypi.douban.com/simple Looking in indexes: https://pypi.douban.com/simple WARNING: Retrying (Retry(total4, connectNone, r…

怎么把图片尺寸在线修改?5种方法调整方式介绍

在日常生活和工作中&#xff0c;我们经常遇到需要调整图片尺寸的情况&#xff0c;无论是为了适应自媒体文章内容中的图片、还是上传社交媒体平台要求&#xff0c;调整图片尺寸是一项非常有用的技能。在本教程中&#xff0c;我们将介绍几个方便快捷的图片处理工具&#xff0c;帮…

c++编程(10)——string

欢迎来到博主的专栏——c编程 博主ID&#xff1a;代码小豪 文章目录 <string>string类的接口构造、析构、与赋值重载构造函数赋值重载运算符 元素访问operator[] 容量修改器对string对象的操作迭代器 std::string是定义在c标准的一个类&#xff0c;定义在标准库<strin…

【JavaEE初阶系列】——Servlet运行原理以及Servlet API详解

目录 &#x1f6a9;Servlet运行原理 &#x1f6a9;Servlet API 详解 &#x1f393;HttpServlet核心方法 &#x1f393;HttpServletRequest核心方法 &#x1f388;核心方法的使用 &#x1f534;获取请求中的参数 &#x1f4bb;query string &#x1f4bb;直接通过form表…

如何更好地使用Kafka? - 事先预防篇

要确保Kafka在使用过程中的稳定性&#xff0c;需要从kafka在业务中的使用周期进行依次保障。主要可以分为&#xff1a;事先预防&#xff08;通过规范的使用、开发&#xff0c;预防问题产生&#xff09;、运行时监控&#xff08;保障集群稳定&#xff0c;出问题能及时发现&#…

【自动驾驶|毫米波雷达】初识毫米波雷达射频前端硬件

第一次更新&#xff1a;2024/5/4 目录 整体概述 混频器&#xff08;MIXER&#xff09; 低通滤波器&#xff08;LPF&#xff1a;Low-Pass filter&#xff09; 数模转换器&#xff08;ADC&#xff1a;Analog to Digital Converter&#xff09; 毫米波雷达功能框图 整体概述 完…

分布式与一致性协议之ZAB协议(六)

ZAB协议 成员发现 成员发现是通过跟随者和领导者交互来完成的&#xff0c;目标是确保大多数节点对领导者的关系没有异议&#xff0c;也就是确立领导者的领导地位。成员发现的实现流程如图所示。 1.领导者选举结束&#xff0c;节点进入跟随者状态或者领导者状态后&#xff0…

微软 AI 研究团队推出 SIGMA:一个开源研究平台,旨在推动混合现实与人工智能交叉领域的研究与创新

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

YzmCMS 7.0任意函数调用RCE 漏洞研究分析

YzmCMS是一款基于YZMPHP开发的一套轻量级开源内容管理系统,YzmCMS简洁、安全、开源、免费,可运行在Linux、Windows、MacOSX、Solaris等各种平台上,专注为公司企业、个人站长快速建站提供解决方案。 YzmCMS 某些接口调用了 db_pdo类的where方法 导致了远程命令执行漏洞&#xf…