Linux gcc和make学习

文章目录

GCC

gcc全程是(GNU compiler collection CNU编译器套件),是由GNU开发的编程语言编辑器,它包含有gcc、g++,但是不仅限于这两种编译器;gcc不仅可以编译c/c++,还可以编译其他的语言包括java等,gcc可以实现跨不同的硬件平台编译,也就是交叉编译,比如在a平台可以编译b平台上的程序,常用支持linux、windows等。

gcc的安装

powershell 复制代码
# 安装必须有管理员程序
# ubuntu
$ sudo apt update # 更新本地下载列表,获取最新的下载地址
$ sudo apt install gcc g++

# centos
$ sudo yum update
$ sudo yum install gcc g++
powershell 复制代码
# 查看gcc版本
$ gcc -v
$ gcc --version

$ g++ -v

gcc的工作流程

gcc编译器对程序的编译主要分为4个阶段:预编译(预处理)、编译和优化、汇编、链接,gcc的编译器可以将这4个步骤合并成一个,下面分别介绍这四个步骤做了什么事情

  • 预编译(预处理):在这个阶段主要做了三件事情:展开头文件、宏替换、去掉注释行,这一阶段由gcc的预处理器完成,最终得到的还是源文件,文本格式。
  • 编译:这个阶段需要调用gcc编译器对文件进行编译,最终得到一个汇编文件
  • 汇编:这个阶段需要调用gcc的汇编器对文件进行汇编,最终得到一个二进制文件
  • 链接:这个阶段需要调用gcc的链接器对程序需要调用的库进行链接,得到一个可以执行的文件

分步完成:比如有一个为file.c的源文件(麻烦版)
预处理 gcc -E file.c -o file.i 编译 gcc -S file.i -o file.s 汇编 gcc -c file.s -o file.o 链接 gcc file.o -o file file.c file.i file.s file.o file可执行文件

一步到位:使用gcc file.c -o aa可以直接生成编译链接后的file的可执行文件aa,直接使用./aa就可以执行。

GCC用到的参数

  • -E:对文件进行预处理,不进行编译,生成的还是源文件
  • -S:对文件进行编译,生成一个汇编文件
  • -c:对文件进行汇编,生成一个二进制文件
  • -o:用来指定生成的文件的名称
  • -I:指定include包含文件的搜索目录

makefile

  • 一般来说,使用gcc的命令行进行程序的编译在单个文件下是非常方便的,但是在实际的应用中,一个工程总不会是由单个文件构成的,往往是由多个文件构成的,当工程的文件逐渐增多,甚至非常庞大,使用GCC进行编译就会变得力不从心,这个时候可以使用make构造工具来完成这个艰巨的任务。
  • make是一个解释makefile中指令的命令工具。make工具在构造项目的时候需要加载一个makefile的文件,makefile关系到整个工程的编译规则,当一个工程中的源文件不计其数,按照类型、功能、模块划分在若干目录中时,这些文件之间存在着各种的依赖关系,makefile可以定义一系列的规则来指定哪一些文件需要先编译,哪一些文件需要后编译,哪一些文件需要重新编译等。
  • mekefile类似于一个shell脚本,所以也可以执行操作系统的一些命令。
  • 使用makefile可以自动化编译,极大的提高了软件开发的效率
  • makefile文件有两种命名的方式makefileMakefile,构建的时候在哪一个目录下执行make命令,哪一个目录的makefile文件就会被加载,因此一个项目中可以有多个makefile文件,分别存放在不同的项目目录中。
powershell 复制代码
# 比如当前目录下有以下文件:add.c,clean,dic.c,head.h,main.c,makefile,sub.c
# 如果是使用gcc来对文件进行编译可以执行一下命令,生成app可执行命令
$ gcc *.c -o app
# 如果使用make命令可以直接
$ make
# 如果编译完成,可以直接使用以下命令删掉所有的编译文件
$ make clean

makefile的规则

复制代码
# 每条规则的语法格式为:
target1,target2,... : depend1.depend2,...
	command
	......
	......

每一条规则都有三个部分组成:目标(target)、依赖(depend)、命令(command)

  • 命令(command):当前这条规则的动作,一般情况下这个动作都是一个shell命令;动作可以是一个也可以是多个,每个命令前都必须有一个Tab缩进并且独占一行

  • 依赖(depend):规则所必须的依赖条件,如果规则不需要任何依赖,那么依赖可以为空;当前的规则可以是其规则的某一个目标,这就形成了规则之间的嵌套;依赖可以有多个

  • 目标(target):规则中的目标,目标与命令是对应的;规则中可以有多个命令,多条命令可以生成多个目标,所以目标可以有多个;通过规则中的命令值执行一个动作,不生成任何文件,这样的目标叫做伪目标

    举一个不太恰当的比喻,目标、依赖、命令的关系就好像是用材料做蛋糕的关系:目标就是我们要做的蛋糕,依赖就是做蛋糕原材料,命令就是用原材料做蛋糕的具体步骤,一个语法正确的规则,往往是一条shell命令对依赖关系的处理得到目标的过程。

powershell 复制代码
# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c -o a.o
# 规则3
b.o:b.c
	gcc -c b.c -o b.o
# 规则4
c.0:c.c
	gcc -c c.c -o c.o

工作原理

  • 在调用make命令编译程序的时候,make会首先找到makefile文件的第1个规则,分析并执行相关的动作。但是需要注意的是,很多时候要执行的动作(命令)中的以来关系是不存在的,如果使用的以来关系不存在这个动作也不会被执行。

  • 遇到以上的情况,首先会将需要的依赖关系生成出来,以此类推,直到makefile的第一条规则的所有的依赖关系都被生成,第一条命令就可以基于这些依赖生成对应的目标,make任务也就结束了。

  • 换句话说,makefile的后续的规则,都是为了第一条命令来服务的。

  • 如果想要执行单条规则,可以使用make 目标名来执行单条规则。
    情况1:依赖存在,但是目标不存在

  • 直接根据依赖生成目标

情况2:目标和依赖都不存在,目标时间戳>依赖时间戳,属于正常情况,不执行

情况3:依赖的时间戳>目标的时间戳,make重新生成目标

自动生成

make有自动推导能力,不会完全依赖与makefile。换句话说,make有一些隐藏的默认规则,这些规则就算不编写在makefile,make也会自动执行对应的规则,生产目标文件。

有的时候,我们可以不使用默认的规则生成目标,比如在编译一个多文件的程序中,如果只使用一条规则对多文件进行编译,当我们修改其中的一个文件时,通过make构建工具还是会把整个的文件全部编译一次,如果文件的数量很多,就会非常消耗时间;但是如果我们把默认规则都列举出来,就可以进行分步编译,当只修改其中一两个文件是,不需要对全部的依赖文件都编译一次,可以大大的提高编译的效率。

如果文件有修改,只编译修改的东西,可以加快编译的速度,只编译对应修改的以来关系的链条

makefile的变量

makefile的变量分为三种:自定义变量、预定义变量和自动变量

自定义变量

用户自己定义的变量叫做用户自定义变量,makefile的变量是没有类型的,直接创建变量的名字然后给其赋值即可

powershell 复制代码
# 正确的定义自定义变量
变量名=变量值

在给makefile的变量赋值之后,可以使用$(变量的名字)来将变量的值取出来

powershell 复制代码
# 用户自定义变量
# 定义一个变量
obj=a.o b.o c.o
# 规则1
app:$(obj)
	gcc $(obj) -o app
预定义变量

makefile中有一些已经定义的变量,用户可以直接使用这些变量,不用进行定义,在某一些条件下,makefile会使用这些预定义的值进行编译,这些预定义变量的名字一般都是大写,经常采用的预定义变量如下表所示:

  • CC:默认值为 cc ,是gcc的意思,一般用来编译c程序
  • CXX:默认值为 g++,一般用来编译c++程序
  • CFLAGS:c语言编译器的编译选项,没有默认值,要用户自己赋值
powershell 复制代码
# 预定义变量
# 定义变量
target=app
obj=a.o b.o c.o
CFLAGS=-O3 # 代码优化(有四种优化级别分别是0-3)
# 规则1
$(target):$(obj)
	$(CC) $(obj) -o $(target) $(CFLAGS)
自动变量

自动变量用来代表这些规则中的目标文件和依赖关系,并且它们只能在规则的命令中使用

  • $@:表示目标文件的名称。包括扩展名
  • $^:在依赖项中,所有不重复的依赖文件,这些文件直接以空格分开
  • $<:在依赖项中的第一个依赖文件的名称
powershell 复制代码
# 自动变量
# 定义变量
target=app
obj=a.o b.o c.o

# 规则1
$(target):$(obj)
	gcc $^ -o $@

模式匹配

通过一个公式来代表若干满足条件的规则,用于精简makefile

以下代码中可以使用通配符%来匹配名字

powershell 复制代码
# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c -o a.o
# 规则3
b.o:b.c
	gcc -c b.c -o b.o
# 规则4
c.0:c.c
	gcc -c c.c -o c.o
powershell 复制代码
# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
%.o:%.c
	gcc -c $< -o $@

函数

makefile中有很多函数并且所有的函数都具有返回值,makefile中的函数写法如下:$(函数名 参数1,参数2,参数3,...)

wildcard函数

这个函数的主要作用是获取指定目录下指定类型的文件名,其返回值是以空格分割的,指定目录下所有符合条件的文件名列表

powershell 复制代码
# 函数原型
$(wildcard PATTERN...)
	参数:指定某个目录,搜索这个路径下指定类型的文件,可以指定多个目录,每一个路径之间用空格隔开

# 返回值:得到若干文件的文件列表,文件名之间使用空格间隔
powershell 复制代码
# 搜索三个不同目录下的.c源格式文件
src=$(wildcard *.c ./sub/*.c /home/robin/b/*.c)
patsubst函数

这个函数的功能是按照孩子定的模式替换指定的文件名的后缀

powershell 复制代码
# 有三个参数,参数之间用逗号隔开
$(patsubst pattern,replacement.text)

# pattern:模式字符串。需要指出要被替换的文件名的后缀是什么,文件名和路径不需要关心,使用 % 即可,如 %.c
# replacement:模式字符串,致命pattern中的后缀最终要被替换为什么;还是使用 % 表示路径和名字,指定新的后缀名即可,比如 %.o
# text:该参数中存储要被替换的原始数据
# 返回值:返回被替换过后的字符串
powershell 复制代码
# 把变量src中所有文件名的后缀从.c替换为.o
src=a.c b.c c.c
obj=$(patsubst %.c,%.o,$(src))

使用函数搜索当前的目录下的c文件,并且修改名字

powershell 复制代码
# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
%.o:%.c
	gcc -c $< -o $@
powershell 复制代码
target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

伪声明

powershell 复制代码
target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

# 这是一条伪声明,没有依赖,也不生成文件(伪目标)
clean:
	rm $(obj) $(target)

对伪目标的声明,以避免make对文件的时间戳进行检测,需要使用.PHONY关键字,声明方式为.PHONY:伪文件名称

powershell 复制代码
# 最终版
target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

# 这是一条伪声明,没有依赖,也不生成文件(伪目标)
.PHONY:clean
clean:
	-rm $(obj) $(target) # 加-表示即使第一条命令执行失败,后面的命令也要继续执行
	...
相关推荐
我好饿117 分钟前
Linux入门教程 第十五章 Linux 系统调优工具
linux·运维·网络
萌虎爱分享23 分钟前
Linux 防火墙 (firewalld) 管理完整指南
linux·运维·防火墙·firewalld
mCell4 小时前
Docker 进阶教程
运维·docker·容器
mCell4 小时前
Docker 入门教程
运维·docker·操作系统
祈祷苍天赐我java之术5 小时前
Linux 进阶之性能调优,文件管理,网络安全
java·linux·运维
ajassi20006 小时前
开源 C++ QT Widget 开发(七)线程--多线程及通讯
linux·c++·qt·开源
孤雪心殇6 小时前
如何安全,高效,优雅的提升linux的glibc版本
linux·后端·golang·glibc
王火火(DDoS CC防护)6 小时前
服务器网络带宽不足要怎么处理?
运维·服务器
潇凝子潇6 小时前
获取服务器指标的信息
linux·运维·服务器
FreeBuf_6 小时前
Chrome高危零日漏洞PoC公开,已被用于野外攻击
linux·运维·服务器·安全·web安全