make和makefile

一、认识make和Makefile

1、会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力


2、一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作


3、makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。


4、make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。


5、make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
 

二、初见make和Makefile

2.1、makefile的创建

这里我创建一个Makefile文件,直接使用 vim makefile,主义makefile的m可以大写也可以小写,我们在写makefile文件时,别打错字母,因为系统只认识makefile和Makefile;

注意: Makefile 文件中,命令行前面是用 tab 键来缩进的,不能用空格空格键
             如果命令行是用 tab 键来缩进,那命令行就会变颜色

 

makefile文件格式:

    目标文件:依赖文件
    			命令
  • 目标文件一般就是最终生成可执行文件,而生成目标文件需要去找他所需的所有依赖文件,如果依赖文件也需要其他的文件,就一直找,直到不需要依赖文件,就好比递归,需要一层一层递归,直到最后不需要为止
  • 这里拿 add.c add.h main.c 来举例,我现在需要把 add.c 和 main.c编译出可执行文件

2.2、make的使用

这里我们就把简单的Makefile文件创建好了,我们来在来创建我们要实现的add.c文件。

我们保存并退出,在命令行输入  make   编译我们的add.c

执行完该Makefile文件后,显示了文件中的执行命令,并且文件中多出了 add.o , main.o 还有我们想要的myadd可执行文件
我们写的这个Makefile中还有一句标签为

clean:
      rm -f *.o myadd

而这里的clean只是一个标签名,我们可以使用make工具来操作这行命令,例如

我们使用 make clean时就会

执行之后,所有 .o 文件和myadd可执行文件也被删除了

三、Makefile中的变量

3.1、自定义变量


Makefile 中允许使用等号自定义变量
Makefile中的变量定义和 shell 变量的定义非常相似,不同的是 Makefile中的变量的名称可以是任何不包含 :, #,= 和空字符的字符序列,并且等号两边可以有空格(shell 定义变量的等号两个不允许出现空格)
变量名=值 --等号两边不能有空格

3.2、变量的调用

在变量标识符前加美元符 $ 来引用,例如

如果你不想看到执行命令时,把makefile中的命令再打印一遍可以在makefile中的命令前加一个@

3.3、变量的赋值

Makefile一共提供了四个赋值运算符 (=、:=、?=、+=)

1).普通赋值 =

如果按照c语言的逻辑,var2应该等于12,但是这里最后打印var2的值为100,所以普通赋值表示赋值最新的变量值

2).立即赋值 :=

立即赋值则是直接赋值,不会在改变

3).询问赋值 ?=

如果该变量前面已经赋过值了,此次就不能再赋值了,如果没有就赋值,只有在该变量为空时才设置值

4.追加赋值 +=

类似字符串连接,将值追加到变量末尾,在追加的时候,自动添加空格

4.4、特殊变量

$^         --表示所有依赖文件
$@         --表示目标文件
$<         --表示第一个依赖文件
%          --通配符,表示所有,和linux中的 * 一样

利用这些特殊变量和自定义变量,我们就可以将Makefile文件写的更加简洁

 四、进度条的实现

我们使用make和makedile工具实现一个进度条的功能

4.1、预备知识

  • 换行和回车的区别
    • \r:回车,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖,如果之前的内容比下一次来的的内容长,就不会被完全覆盖
    • \n:换行,换到当前位置的下一行,而不会回到行首
  1#include <stdio.h>
  2 int main()
  3 {
  4     printf("hello world\n");                                                                                                                               
  5     return 0;
  6 }

当然上面的实现是可以打印出 hello world

#include <stdio.h>
int main()
{
   printf("hello world\r");                                                                                                                               
   return 0;
}

第二个是打印不出来hello world

不难发现 \n 可以打印出来,而 \r,不能打印出来,因为显示器模式是行刷新缓冲区是按行缓冲的,没有\n,就不能立即刷新。 \r 回到行首后,会进行覆盖写,shell 提示符会覆盖掉之前写的 “hello world”,如果我们在 “hello world” 不加 \r,则不会进行覆盖写,shell 提示符会顺着 “hello world” 往后写。


缓冲区的理解

行缓冲是缓冲区刷新策略的一种,在行缓冲模式下,当输入和输出中遇到 ‘\n’ 换行时,就会刷新缓冲区,下面我们认识头文件<unistd.h>的三个函数

sleep : Linux 下的休眠函数,单位是秒
u s l e e p usleepusleep:和sleep 一样,单位ms(即10-6 m)
f f l u s h fflushfflush :刷新缓冲区

  1 #include <stdio.h>
  2 #include <unistd.h>                                                                                                                                       
  3 int main()
  4 {
  5     printf("hello world");
  6     sleep(3);
  7     return 0;
  8 }

我们知道函数代码语句是从上到下依次进行的,而我们看到的却是先休眠三秒,然后再打印出"hello world",原因是因为数据保存在缓冲区中,没有主动刷新。当程序退出后,保存在缓冲区中的数据被自动刷新出来了,如果我们想提前刷新,便可以调用f f l u s h fflushfflush函数来刷新缓冲区。

  1 #include <stdio.h>
  2 #include <unistd.h>                                                                                                                                       
  3 int main()
  4 {
  5     printf("hello world");
  6		fflush(stdout);
  7		printf("\n");
  8     sleep(3);
  9     return 0;
  10}


 

4.2进度条的实现

我们需要以下文件

main.c文件实现:宏定义,参数设置,函数调用和头函数包含

当然我们实现进度条都是要在一个场景下实现的,这里我模拟实现了一个下载的场景

  1 #include"processbar.h"
  2 #include<time.h>
  3 #include<stdio.h>
  4 #include<unistd.h>
  5 
  6 #define FILESIZE 1024*1024*1024
  7 //模拟一种场景,表示一种下载的任务
  8 void download(callback_t cb)
  9 {
 10     srand(time(NULL)^1023);
 11     int total=FILESIZE;
 12     while(total)
 13     {
 14         usleep(10000);    //下载动作
 15         int one = rand()%(1024*1024*10);
 16         total-=one;
 17         if(total<0)
 18         {                                                                                                                                          
 19             total=0;
 20         }
 21         int download=FILESIZE-total;
 22         double rate=(download*1.0/(FILESIZE))*100.0;  //0 23.4   45.6  67.5
 23         cb(rate);
 24     }
 25 }
 26 int main()
 27 {
 28   // process();
 29    download(process_flush);
 30     return 0;
 31 }
~
~

processbar.h文件实现

  1 #pragma once                                                                                                                                       
  2 #include<stdio.h>
  3 #include <stdlib.h>
  4 #define NUM 103
  5 #define Body '='
  6 #define Head '>'
  7 typedef void (*callback_t)(double);
  8 
  9 
 10 //version1
 11 void process();
 12 
 13 //version2
 14 void process_flush(double rate);
~

processbar.c文件实现

  1 #include"processbar.h"
  2 #include<string.h>
  3 #include<unistd.h>
  4 
  5 const char*lable="|/-\\";
  6 void process()
  7 {
  8     char buffer[NUM];
  9     memset(buffer,'\0',sizeof(buffer));
 10     int cnt=0;
 11     int n=strlen(lable);
 12     buffer[0]=Head;
 13     while(cnt<=100)
 14     {
 15         printf("[%-100s][%3d%%][%c]\r",buffer,cnt,lable[cnt%n]);
 16         fflush(stdout);
 17         buffer[cnt++]=Body;
 18         if(cnt<100)
 19         {
 20             buffer[cnt]=Head;
 21             usleep(500000);
 22         }
 23     }
 24     printf("\n");
 25 }
 26 
 27 //version2
 28 //进度是多少,进度条是不知道的。,另外,进度应该是依附于一个东西的,比如下载
 29 char buffer[NUM]={0};
 30 void process_flush(double rate)
 31 {
 32     static int cnt =0;
 33     int n=strlen(lable);                                                                                                                           
 34     if(rate<=1.0)
 35     {
 36         buffer[0]=Head;
 37     }
 38     printf("[\033[4;32;44m%-100s\033[0m][%.1f%%][%c]\r",buffer,rate,lable[cnt%n]);
 39     fflush(stdout);
 40 
 41     buffer[(int)rate]=Body;
 42     if((int)rate+1<100)
 43     {
 44         buffer[(int)(rate+1)]=Head;
 45     }
 46     if(rate>=100.0)
 47     {
 48         printf("\n");
 49     }
 50     cnt++;
 51     cnt%=n;
 52 }

makefile文件的实现

   processbar:main.o processbar.o                                                                                                                     
       gcc -o $@ $^
   main.o:main.c
       gcc -c main.c
   processbar.o:processbar.c
       gcc -c processbar.c
   
  .PHONY:clean
   clean:
      rm -f main.o processbar.o processbar

效果展示:

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城免费搭建

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

Windows10关闭系统自动更新

1.背景 2.步骤 第一步: 第二步: 完美

笔记本电脑没有声音?几招恢复声音流畅!

笔记本电脑已经成为我们日常生活和工作的重要工具&#xff0c;而其中的声音是其功能之一。然而&#xff0c;有时您可能会遇到笔记本电脑没有声音的问题&#xff0c;这可能是由多种原因引起的。在本文中&#xff0c;我们将深入探讨笔记本电脑没有声音的常见原因&#xff0c;并提…

jbase实现通用码表

没有通用码表的体系是不完美的&#xff0c;当年我用C#能实现的通用码表&#xff0c;现在在java一样的实现了&#xff0c;通用码表对提高开发效率和降低开发成本的作用巨大&#xff0c;开发可以专注写业务&#xff0c;而不必被太多的维护界面束缚。进而体现在产品竞争力上面&…

加密狗作用是什么?工作原理及使用方法

加密狗是一种用于软件保护的硬件设备&#xff0c;通常被用于防止软件被非法复制、篡改或者恶意使用。以下是加密狗的作用、工作原理及使用方法&#xff1a; 作用 加密狗的主要作用是提供软件保护&#xff0c;它能够通过加密算法对软件进行加密&#xff0c;以防止软件被非法复制…

从0开始学习JavaScript--JavaScript 类和模块详解

JavaScript的类和模块是现代Web开发中的重要组成部分&#xff0c;它们提供了一种更面向对象的编程方式和模块化的组织代码方式。本文将深入探讨JavaScript中类和模块的各个方面&#xff0c;并通过丰富的示例代码来帮助大家更好地理解和运用这些概念。 1. 类的基本概念与语法 …

Linux编译器:gcc/g++的使用

我们在学习编译器时&#xff0c;我们不仅要只会使用编译器&#xff0c;还要理解程序的编译过程。一个程序存在两个不同的环境。第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令&#xff1b;第2种是执行环境&#xff0c;它用于实际执行代码。本篇文章将…

Linux C 进程间通信

进程间通信 概述进程间通信方式管道概述管道函数无名管道 pipe有名管道 makefifo删除有名管道 rmove 有名管道实现 双人无序聊天 例子 信号信号概述信号处理过程信号函数传送信号给指定的进程 kill注册信号 signal查询或设置信号处理方式 sigaction设置信号传送闹钟 alarm 有名…

【ONE·Linux || 网络基础(三)】

总言 主要内容&#xff1a;HTTP和HTTPS工作方式简述。 文章目录 总言6、HTTP协议&#xff08;应用层二&#xff09;6.1、入门认识6.1.1、认识URL6.1.2、urlencode和urldecode 6.2、快速构建6.2.1、快速构建http请求和响应的报文格式6.2.2、http demo6.2.2.1、sock.hpp &&a…

vite环境变量相关

环境变量&#xff1a;根据环境的不同&#xff0c;灵活的自动读取相应的变量。避免了手动修改。 import path from path import postCssPxToRem from postcss-pxtorem import { defineConfig, loadEnv } from vite import createVitePlugins from ./vite/plugins import copy f…

【LeetCode刷题笔记】二叉树(三)

701. 二叉搜索树中的插入操作 解题思路: 1. 模拟 ,如果 根节点为空 ,就用 插入值创建根节点 直接返回。否则, cur 从 根节点 开始,比较 当前节点的值和插入值的大小关系 : 1)如果 插入值 < cur ,就

一张图系列 - “position_embedding”

关于位置编码&#xff0c;我感觉应该我需要知道点啥&#xff1f; 0、需要知道什么知识&#xff1f; multi head atten 计算 复数的常识 1、embedding 是什么&#xff1f; position embedding常识、概念&#xff0c;没有会怎样&#xff1f; 交换token位置&#xff0c;没有P…

vue手动搭建脚手架(保姆式教案)

目录 1.创建项目 1.node.js环境搭建 2.安装vue-cli 3.搭建项目 目录结构 1.创建项目 1.node.js环境搭建 下载安装node.js&#xff08;Download | Node.js&#xff09;&#xff0c;安装时不要安装在C盘Windowsr打开cmd管理工具开始输入命令检查node.js是否安装和版本号&a…

在IDEA中的DeBug调试技巧

一、条件断点 循环中经常用到这个技巧&#xff0c;例如&#xff1a;遍历1个List的过程中&#xff0c;想让断点停在某个特定值。 参考上图&#xff0c;在断点的位置&#xff0c;右击断点旁边的红点&#xff0c;会出来1个界面&#xff0c;在Condition这里填写断点条件即可&#…

QCustomPlot的下载和使用

0.QCustomPlot介绍 QCustomPlot是一个基于Qt画图和数据可视化的C控件。在Qt下的绘图工具有Qwt、QChart和QCustomPlot&#xff0c;置于选择哪个绘图工具各有优缺点。 在绘制大量数据&#xff08;10万个点以上&#xff09;时选择QCustomPlot&#xff0c;在数据量比较小时&#x…

docker内更新显卡cuda cudnn

当前docker使用的cuda为10.2&#xff0c;为保证服务器环境使用相同的cuda版本&#xff0c;需对cuda版本进行升级&#xff0c;时间长了忘记如何操作&#xff0c;此处记录一下&#xff1a; *docker内使用的cuda版本低于容器外的显卡驱动版本即可&#xff0c;此处不对显卡驱动进行…

解决STM32F429烧录程序后还需复位才能植入程序的bug

1.打开魔术棒&#xff0c;打开debug 2.打开setting 3.打开Flas Download 4.开启Reset and Run 5.点进去Pack选项页面&#xff0c;去掉enable

springboot 2.1.0.RELEASE 项目加入swagger接口文档

Release v2.1.0.RELEASE spring-projects/spring-boot GitHub springboot 2.1.0.RELEASE发行日期是2018年10月30日&#xff08;Oct 30, 2018&#xff09; 不要使用过高的swagger版本&#xff0c;如SpringFox Boot Starter 3.0.0&#xff0c;否则报错&#xff1a; spring-…

MOS管体电极接源端版图layout画法

记录一个lvs一直跑不通的问题。 问题描述&#xff1a;lvs一直显示某几个MOS管的体电极连接问题。连线没有问题&#xff0c;版图中已经画了衬底。 原因&#xff1a; 图中四个管子的衬底接了源端&#xff0c;没接电源。 解决办法&#xff1a; 法1、源端接地 法2、将这四个管子…

msvcp140.dll丢失的解决办法,msvcp140.dll丢失会导致电脑出现哪些错误

msvcp140.dll丢失的解决办法都有哪些&#xff1f;如果电脑不及时将msvcp140.dll文件进行修复的话电脑可能会出电脑可会出现哪些错误&#xff1f;今天就和大家说说都有哪些解决办法有效的解决msvcp140.dll丢失的问题。 一.缺失msvcp140.dll会有什么问题 当电脑上缺少msvcp140.d…