Linux-GCC介绍+入门级Makefile使用

news2024/11/13 16:02:45

前言

(1)我们都知道,在Linux中编译.c文件需要使用 gcc -o .c文件的指令来将C文件变成可执行文件。但是我们有没有发现,如果我们需要编译大一点的工程,后面需要加上的.c文件是不是太多了?感觉非常的麻烦。
(2)那有没有什么方便的方法,帮助我们编译大型文件呢?有,也就是本章需要介绍的Makefile工具。需要注意的是,Makefile工具如果真的想全部学,内容很多想学的看 Makefile详细英文文档或 GNU Make 使用手册(于凤昌中译版)。 本文仅用于新手小白学习
(3)本文将 简单介绍GCC的编译流程,然后再讲解Makefile入门级使用

GCC编译流程

单个.C文件变成可执行文件

简单介绍

(1)首先我们需要知道, 我们写的.c代码是无法直接使用的。如果是搞单片机的同学们都知道,一个.c文件需要先再keil这种编译器中先编译,然后 将生成的hex文件烧录到单片机中。(stlink那个虽然只需要我们点一下烧录就可以,但是实际上也是这么做的)
(2)那么这个hex文件是什么东西呢?我们打开hex文件发现,里面都是16进制的数字,而这些代码才是机器真正能够识别的代码。
(3)那么从.c文件到这些机器可执行的过程具体是什么呢?四个步骤: 预处理--> 编译 --> 汇编 --> 链接
(4)但是在 日常生活中通常使用“编译”统称这 4 个步骤,不是特指这 4 个步骤中的某一个。 有时候也会有人将前三步称为编译,讲法不一,只要知道.c文件到机器可执行文件有四个步骤即可。

预处理

(1)预处理:在C/C++源文件中, 以“ #”开头的命令被称为预处理命令。预处理需要将
<1> 包含的文件放入原文件中。比如一个main.c中第一行写了#include<stdio.h>,那么预处理阶段就需要将stdio.h文件包含到main.c中,上面一大段就是stdio.h文件的内容。
<2> 宏定义展开。比如我们此刻宏定义了printf为CSDN,所以在.i文件中CSDN部分变成了printf。
<3> 根据条件编译命令选择要使用的代码,最后将这些东西输出到一个“ .i”文件中等待进一步处理
(2)格式: gcc -E .c文件 -o .i文件
(3)需要注意, 如果是直接 gcc -o 可执行文件名 .c文件 ,那么是直接完成了预处理,编译,汇编,和链接的四个步骤。而如果是加上-E,就是只执行预处理

编译

(1)编译就是把 C/C++代码(比如上述的“.i”文件)“ 翻译” 成汇编代码,所用到的工具为 cc1。(名字为CC1,x86和arm板都有自己的CC1命令)。
(2)格式: gcc -S .c或者.i文件 -o .s文件
(3) 程序的语法错误是在这一阶段进行判断的

汇编

(1)汇编:将汇编代码翻译成符合一定格式的机器代码(二进制代码,我们上面看到的是十六进制,稍微转换一下就是二进制了)。在Linux 系统上一般表现为 ELF 目标文件(OBJ 文件),用到的工具为 as。 x86 有自己的 as 命令, ARM 版也有自己的 as 命令,也可能是 xxxx-as(比如 armlinux-as)。
(2)格式: gcc -c .c文件/.i文件/.s文件 -o .o文件
(3)反汇编:将机器代码转换为汇编代码,这在调试程序时常常用到。

链接

(1)链接就是将上步生成的 OBJ 文件和系统库的 OBJ 文件、 库文件链接起来,最终生成了可以在特定平台运行的可执行文件,用到的工具为 ld 或 collect2。
(2)格式: gcc -o 可执行文件 .c/.i/.s/.o文件都可以

多个.c文件编译

编译多个.c文件

(1)经过上面简介,我们对gcc编译流程有了简单的了解。那么问题来了,我们做项目不可能就只有一个.c文件,很可能是几十个.c文件,一个一个gcc -o这样编译很麻烦。那么我们介绍一下如何同时编译多个.c文件。
(2)格式: gcc -o 可执行文件 .c文件 .c文件 ...(后面接多个.c文件都行)

修改项目中一个.c文件

(1)经过上面这么一说,发现随着.c文件的增加,后面需要写入的.c也会更多,有些许麻烦。但是这不是最让人感到头痛的事情。
(2)假设我们突然发现这个文件需要修改,将其中一个.c文件进行修改之后,那么重新编译将会变的非常的麻烦。所以说,非常麻烦。这个时候Makefile的作用就体现出来了。

Makefile介绍

Makefile需要做到的效果

(1)在Linux中,没有比较好的图形化编译工具。如果我们是在windows上编写程序,以keilMDK举例,只用点击左上方的编译按键即可编译程序。
(2)而且在编译工程中,我们会发现。如果我们按左边这个按键,已经编译了一次程序之后,第二次即使有些许改动,编译就会非常快。而他右侧的这个,即使程序没有修改,编译也会非常慢。为什么呢?
因为 第一个按键只会编译修改部分,而第二个按键无论你有没有修改文件,都会重新编译一次。所以我比较喜欢使用第一个编译按键,我们这章需要讲的也是要实现这个功能。
(3) Windows中的编译非常简单,但是其实他内部的原理就是Makefile

Makefile语法简单介绍

Makefile格式使用方法介绍

(1)Makefile非常简单,格式如下:
目标:依赖
【Tab】命令
(2)目标和依赖都是文件,为什么起这么奇怪的名字,可能是从英文直翻的。 上面这一段就像C语言的if函数,他作用就是比较“依赖文件”和“目标文件”的更新时间。如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目标文件”
(3) 命令被执行的 2 个条件:依赖文件比目标文件新,或是目标文件还没生成
(4)Makefile的文件名必须是Makefile或者makefile。
(5)在Makefile中,想要执行命令,格式是: make 目标。但是我们常常看到很多人只输入了一个make,因为如果我们不指定目标的话, 默认执行的是第一个目标所对应的规则。(这个不理解的话看下面的图文解释)
//将上述Makefile格式用C语言的形式进行表示
if(“依赖文件”更新时间 > “目标文件”更新时间)
    执行命令

Makefile图文介绍

执行Makefile的图文解释:
1,前面说了, 使用 make 目标来编译, 如果我们不指定目标的话, 默认执行的是第一个目
标所对应的规则所以make与make test是等效的
这个命令执行流程是:(1)首先判断test文件和main.o与test.o的更新时间。因为此时test文件不存在,所以命令可以执行。(可以看前面说的命令执行的两个条件)(2)因为main.o与sub.o是由main.c与sub.c而来的。所以他们又会去执行make main.o和make sub.o。因为make sub.o中,依赖文件s.c不存在,所以无法比较,程序终止。
2,当"目标文件"比"依赖文件"新的时候,命令不会执行。而main.o在make test中,已经执行了一次make main.o。而main.c没有被修改过,所以这里提示main.o已经是最新的了。
3,命令执行的条件有,依赖文件比目标文件新,或者目标文件不存在。因为命令中,永远不会产生目标文件,所以make cleal与make s.o永远可以执行。
4,需要注意的是, make clean没有依赖文件为什么可以执行是因为在程序先判断clean文件是否存在,发现不存在,所以就直接执行程序,而不是去找依赖文件了。 与第一问的makesub.o不同,需要注意。

Makefile伪目标--避免与当前目录下的同名文件

(1)上面我们说了,使用make clean可以将所有编译过程中产生的文件全部删除,但是其中存在问题。假如当前文件夹下也存在一个叫做clean的文件名怎么办呢?
这种情况能够做的办法有很多种,比如 <1>将clean文件名改一下,或者是 <2>将makefile中的目标文件clean改成当前目录下不存在的一个文件名。不过现在我要说的方法是,伪目标的方法。
格式: .PHONY: 目标

Makefile使用实战

最简单编写方式和使用(1)

(1)上面我们说了,假如一个项目有几千上万的.c文件,而我们只更改一个.c文件。对每一个文件都进行预处理,编译,汇编和链接,效率低下。
(2)所以,我们是不是可以先让每一个.c文件,先进行前三步,最后在进行一次链接呢?这样做,就 可以让我们就可以先判断.c文件与.o文件(.o文件就是执行了前三步骤后生成的)更改时间。
如果.o文件比.c文件更改时间更新,那么就表示这一个.c文件没有被修改。如果.c文件比.o文件更改时间更新,那么表示这一个文件被修改过了,于是重新进行一次预处理,编译和汇编。
最后将最终的可执行文件名与所有.o文件的更改时间做对比,如果可执行文件更改时间比.o文件的新,那么就说明这个项目文件就没有被修改过。如果所有.o文件中有一个.o文件比可执行文件更改时间更新,那么就将所有.o文件重新进行一次链接。
(3) 需要注意,命令部分前面需要使用TAB键进行缩进,不能是空格!!!
(4)当文件夹中有了Makefile(makefile也可以)文件之后,只需要输入make命令就可以执行Makefile文件了。

进阶编写(2)-通配符和自动化变量

(1)有了Makefile文件,我们对文件进行编译方便了很多。但是我们还是觉得麻烦,因为我们每一个项目都需要重新进行一次编写Makefile。而且如果项目文件一多,编写Makefile也极其花费时间。
(2)于是我们可以使用通配符和自动化变量,比如前面的main.o和sub.o都可以统一是使用%.o表示,main.c和sub.c统一使用%.c表示。

$@

表示所有目标文件

$<

表示第一个依赖文件, 如果依赖模式是%, 那么它就表示一系列文件。(%为通配符, 类似 linux上的 *)

$^

表示所有依赖文件

%.c

表示所有.c文件

%.o

表示所有.o文件

第一行不能使用%.o

虽然%.o可以表示所有的.o文件,但是 在第一行不能使用%.o代替,必须一个一个的.o文件写出来! 这个时候有人会抱怨还是太麻烦了,别慌,后面讲解进阶编写(3)的时候会有解决办法

正常写法

进阶编写(3)--自动包含.o文件

(1)根据上面的讲解之后,我们会发现,此时的Makefile文件已经比我们之前直接GCC编译好很多了。但还是可以完善,就是让他自动包含.o文件。这样才可以真正的实现我们一开始说的,模拟windows中的编译按键。
(2) 在这里我会先讲解Makefile的变量及其变量赋值,然后再讲解Makefile 的函数,最后才会说到如何编写最终版的Makefile文件

变量及变量赋值

这部分主要是有了解就行,不然最终的进阶编写看不懂。

=

延时变量

:=

立即变量

?=

延时变量,只有第一次定义时赋值才成功;如果曾定义过,此赋值无效

+=

使用“+=” 赋值是追加赋值, 是在我们前面定义的好的字符串里面在添加进去新的字符串

=示例

使用“=” 来赋值, 是延迟赋值。按照正常的思路,A=a,那么第二行B就应该是a。但是因为再Makefile中"="是延迟赋值,所以B最终的值是b

:=示例

使用“ : =” 给变量赋值, 是立刻赋值。这个就可以按照正常的C语言执行流程来,变量跑到那里,赋值就是那里,不会因为后面的赋值而导致变量数值改变。

?=示例

如果前面赋值了,就是之前的值(即a),如果前面没有被赋值(即第一行没有),就是当前值(即b)。

+=示例

使用“+=” 赋值是追加赋值, 是在我们前面定义的好的字符串里面在添加进去新的字符串,不过中间会有空格。

Makfile函数

wildcard 函数

格式: $ (wildcard PATTENR)
功能: 展开指定的目录,文件名之间用一个空格隔开
解释:比如我们需要查找当前目录,或者子目录下的某一种文件。类似于Windows中,有时候需要在一个目录中选中目标文件,可以选择只显示某一种类型文件,这样方便我们查找到目标文件。

notdir函数

格式: $ (notdir $ (var) )
功能: 去掉路径。
解释:上面查找到文件之后,会显示文件的路径。假如我们不想看文件路径,就可以使用这个函数。

dir函数

格式: $(dir <names...>)
功能: 取出目录, 这里的目录指的是最后一个反斜杠/ 之前的部分, 如果没有反斜杠/就返回当前。
解释:这个函数和notdir恰恰相反,notdir是将目录去除留下目标文件。而dir是将目标文件去除,留下路径。

patsubst函数

格式: $(patsubst 原文件, 目标文件, 文件列表)
功能: 替换文件后缀
解释:如果文件列表里面有main.c,sub.c,hello.i。三个文件,将原文件.c文件替换成.o文件,最后文件列表的文件就是main.o,sub.o,hello.i。

foreach函数

格式: $(foreach <var>,<list>,<text>)
功能:把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text> 所包含的表达式。 每一次 <text> 会返回一个字符串
解释:这个函数可以理解为C语言中的printf函数。C语言中的printf(“A=%d”,3),3就是list,A=%d就是test,而var就是%d。

实操进阶编写

上面讲了这么多,终于到了实操了。首先,我们先展开当前目录下的所有.c文件,然后将这些.c文件的前面的目录去除,之后再将.c替换成.o( 注意,实际的文件名没有被替换)。最后将var3的值放在test之后即可。

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

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

相关文章

linux篇【16】:传输层协议<后序>

目录 六.滑动窗口 &#xff08;1&#xff09;发送缓冲区结构 &#xff08;2&#xff09;滑动窗口介绍 &#xff08;3&#xff09;滑动窗口不一定只会向右移动。滑动窗口可以变大也可以变小。 &#xff08;4&#xff09;那么如果出现了丢包, 如何进行重传? 这里分两种情况…

「微报告」激光雷达前装“成色”

激光雷达正处于第一波规模上车期。 高工智能汽车研究院监测数据显示&#xff0c;2022年中国市场&#xff08;不含进出口&#xff09;乘用车前装标配搭载激光雷达12.99万台&#xff0c;配套车辆11.18万辆&#xff0c;同比分别增长1544.30%和2626.82%。此外&#xff0c;提供选装的…

华为OD机试题,用 Java 解【相对开音节】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

基于BERT-PGN模型的中文新闻文本自动摘要生成——文本摘要生成(论文研读)

基于BERT-PGN模型的中文新闻文本自动摘要生成&#xff08;2020.07.08&#xff09; 基于BERT-PGN模型的中文新闻文本自动摘要生成&#xff08;2020.07.08&#xff09;摘要&#xff1a;0 引言相关研究2 BERT-PGN模型2. 1 基于预训练模型及多维语义特征的词向量获取阶段2. 1. 1 BE…

Python3-数据结构

Python3 数据结构 本章节我们主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的&#xff0c;这是它区别于字符串和元组的最重要的特点&#xff0c;一句话概括即&#xff1a;列表可以修改&#xff0c;而字符串和元组不能。 以下是 Python 中列表的…

3.3 GPIO输入

按键1.1 按键介绍•按键&#xff1a;常见的输入设备&#xff0c;按下导通&#xff0c;松手断开•按键抖动&#xff1a;由于按键内部使用的是机械式弹簧片来进行通断的&#xff0c;所以在按下和松手的瞬间会伴随有一连串的抖动1.2 按键的四种接法P1是最常用的接法&#xff0c;p1…

【小程序】django学习笔记1

网页能用&#xff0c;不知道小程序能不能用。应该能吧。。。。。创建django程序文件包&#xff0c;xxx处是给该文件夹起个名django-admin startproject xxx一个project是由很多个app&#xff08;小应用&#xff09;组成的在文件夹目录下创建一个app&#xff0c;xxx处给该app起个…

脑雾到来的6个症状

在阳康后的一段时间里&#xff0c;很多人发现自己脑力不似从前&#xff0c;长时间的修养也没能彻底恢复&#xff0c;以为自己有后遗症&#xff0c;十分紧张。其实&#xff0c;脑雾不是专业的医学名词&#xff0c;它是对一类症状的统称&#xff1a;健忘、注意力不集中、思维迟钝…

第17天-整合Redis缓存改造三级分类,并解决缓存击穿、穿透、雪崩、一致性问题

1.缓存 1.1.缓存使用 为了系统性能的提升&#xff0c;一般都会将部分数据放入缓存中&#xff0c;达到快速响应的目的。而数据库承担数据落盘工作。 哪些数据适合放入缓存&#xff1f; 即时性、数据一致性要求不高的访问量大且更新频率不高的数据&#xff08;读多&#xff0c…

布隆过滤器Bloom -> Redisson系列-3

接上文&#xff1a;RBucket对象桶 -&#xff1e; Redisson系列-1&#xff08;让redis操作更优雅&#xff09;_子书少卿的博客-CSDN博客 本质上布隆过滤器是一种数据结构&#xff0c;是一种比较巧妙的概率性数据结构&#xff0c;特点是可以高效的插入和查询&#xff0c;可…

浅谈一下虚拟机栈

虚拟机栈 概述 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多的…

SpringBoot学习笔记(二)配置文件

1、配置文件SpringBoot使用一个全局的配置文件&#xff0c;配置文件名是固定的&#xff1b;application.propertiesapplication.yml配置文件的作用&#xff1a;修改SpringBoot自动配置的默认值&#xff1b;SpringBoot在底层都给我们自动配置好&#xff1b;YAML&#xff1a;以数…

MySQL常用函数整理

MySQL常用函数整理sql函数分类一、流程控制1、判断值为null或空字符串2、IF函数3、IFNULL函数4、CASE函数(1) 相当于switch case的作用(2) 相当于if elseif的作用5、COALESCE函数二、字符串类&#xff08;GBT答案&#xff09;1、用于select、insert等子句中2、用于where子句中其…

来吧!接受Kotlin 协程--线程池的7个灵魂拷问

前言 之前有分析过协程里的线程池的原理&#xff1a;Kotlin 协程之线程池探索之旅(与Java线程池PK)&#xff0c;当时偏重于整体原理&#xff0c;对于细节之处并没有过多的着墨&#xff0c;后来在实际的使用过程中遇到了些问题&#xff0c;也引发了一些思考&#xff0c;故记录之…

从零开始学习c语言|21、动态内存管理

一、malloc函数 1、什么是malloc函数 malloc是memery(内存)和allocate(分配)的缩写&#xff0c;顾名思义&#xff0c;malloc函数为动态分配内存的意思 2、malloc函数语句 int *p(int *)malloc(sizeof(int))malloc函数的形参为申请的内存空间大小&#xff0c;上述申请了一个i…

“巨亏成名”的魔鬼交易员,你知道几个?

谁说在期货市场上只有赚大钱才能出名&#xff1f;殊不知还有这样一群特殊的交易员靠着巨额亏损而“一战成名”&#xff0c;亏得是老东家元气大伤&#xff0c;外号“魔鬼交易员”——“不亏不成魔”&#xff01;接下来火象就给大家盘点几位代表性魔鬼交易员&#xff0c;看看他们…

论文阅读 | 轻量级网络 MobileNets/Xception/ShuffleNet

前言&#xff1a;重温经典&#xff0c;整理了一些几年前做轻量级网络的论文&#xff0c;其中的深度可分离卷积和通道shuffle的思想至今也在沿用 &#xff08;这几天都没看论文然而实验还是没跑出来&#xff0c;不卷会议了&#xff0c;开始摆烂…&#xff09; 论文地址&#xff…

JavaScript Math 算数对象

文章目录JavaScript Math 算数对象Math 对象Math 对象属性Math 对象方法算数值算数方法JavaScript Math 算数对象 Math&#xff08;算数&#xff09;对象的作用是&#xff1a;执行常见的算数任务。 Math 对象 Math&#xff08;算数&#xff09;对象的作用是&#xff1a;执行普…

规划数据指标体系方法(上)——OSM 模型

之前我已经有写过文章讲了数据指标体系的搭建思路&#xff0c;但有同学还是不太清楚要从何入手&#xff0c;今天我就来跟大家讲一讲搭建数据指标体系之前第一步要先做的事情——规划数据指标体系。 规划数据指标体系&#xff0c;在业内有三种比较常见的方法&#xff0c;分别是&…

MySql索引失效的情况

文章目录1、对索引字段进行了运算或者使用了函数2、查询的数据类型与字段类型不一致3、违反了索引的最左匹配原则4、全表扫描更快5、where语句中索引列使用了负向查询&#xff0c;可能会导致索引失效6、索引字段可以为null&#xff0c;使用is null或is not null时&#xff0c;可…