初识Linux系统下C编程,gcc及Makefile使用,系统为Ubuntu16.04LTS
流程
C语言代码编写流程:
- 代码编写
- 编译代码
使用Vim进行代码编写,编写完成以后在使用gcc编译器进行编译。
代码编写
用户根目录下新建C_Project目录用于存放C代码
1 | |
进入C_Project
1 | |
新建HelloWorld目录存放此次代码
1 | |
进入HelloWorld目录
1 | |
Vim新建并打开main.c
1 | |
编写代码如下:

保存退出
查看main.c
1 | |

编译代码
使用gcc工具编译main.c
1 | |
编译完成使用ls查看文件,出现a.out文件,a.out就是编译生成的可执行文件,a为默认名
1 | |
执行a.out
1 | |
输出运行结果无误

指定可执行文件为main
1 | |

gcc编译器
Ubuntu下的C语言编译器是GCC,GCC编译器在安装Ubuntu的时候就已经默认安装好了。
查看GCC编译器的版本号:
1 | |

输入命令“gcc -v”命令以后,你的终端输出版本信息,那么说明你的电脑已经有GCC编译器了。最后下面的“gcc version 5.4.0”说明本机的GCC编译器版本为5.4.0的。“Target: x86_64-linux-gnu”说明Ubuntu自带的GCC编译器是针对X86架构的,因此只能编译在X86架构CPU上运行的程序。如果想要编译在ARM上运行的程序就需要针对ARM的GCC编译器,也就是交叉编译器。
gcc命令
1 | |
1 | |
编译错误警告:
若代码有bug,gcc编译命令不通过,输出错误所在文件行列及信息
gcc编译流程
GCC编译器的编译流程是:预处理、编译、汇编和链接。
-
预处理:展开所有的头文件、替换程序中的宏、解析条件编译并添加到文件中。
-
编译:将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译。
-
汇编:将汇编语言文件编译成二进制目标文件。
-
链接:将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉及到静态库和动态库等问题。
Makefile
描述哪些文件需要编译、哪些需要重新编译的文件就叫做Makefile,Makefile就跟脚本文件一样,里面还可以执行系统命令。使用的时候只需要一个make命令即可完成整个工程的自动编译,极大的提高了软件开发的效率,通过Makefile你也能了解整个工程的处理过程。
预期功能:
1、如果工程没有编译过,那么工程中的所有.c文件都要被编译并且链接成可执行程序。
2、如果工程中只有个别C文件被修改了,那么只编译这些被修改的C文件即可。
3、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的C文件,并且链接成可执行文件。
Makefile引入
新建目录存放代码文件

vim新建编辑main.c,input.c,input.h,draw.c,draw.h 5个文件
main.c
1 | |
input.c
1 | |
input.h
1 | |
draw.c
1 | |
draw.h
1 | |
完成后按基本步骤使用gcc编译成可执行文件main并执行,输入行数及字符,输出三角形

很容易发现当存在多个.c/.h文件时,使用gcc编译命令过于复杂
如果只编译被修改过的文件,文件数量太多时不方便
所以我们需要使用Make工具解决这个问题
新建Makefile文件并编辑,注意行首缩进必须用TAB
1 |
|
使用make命令编译,执行main,输入输出无误,使用make clean清除执行文件

Makefile规则格式
Makefile里面是由一系列的规则组成的,这些规则格式如下:
1 | |
对于这里的Makefile,有5条规则
1 |
|
make命令执行过程:
- 在当前目录查询Makefile文件,按照Makefile文件执行
- 更新main,依赖于main.o input.o draw.o,所以更新这三个文件,更新完执行命令链接编译.o文件
- 更新main时会更新main.o input.o draw.o,查询并执行2,3,4条规则,更新.c文件并不链接编译.c文件
- clean没有依赖文件,故make时不执行,make clean时执行命令删除.o文件及main文件
- 对应文件未修改时不会执行对应编译命令
Makefile变量
Makefile中的变量都是字符串,类似C语言中的宏。
这里将main.o input.o draw.o赋值给变量Objects,引用时使用$(Objects)
1 | |
| 赋值符: | 功能 |
|---|---|
| = | 使用“=”在给变量的赋值的时候,使用最后一次变量定义的值 |
| := | 使用前面已经定义好的值 |
| ?= | 如果变量前面没有被赋值,那么此变量就是当前赋值,如果前面已经赋过值了,那么就使用前面赋的值。 |
| += | 给前面已经定义好的变量添加一些字符串进去 |
Makefile模式规则
通过模式规则我们就可以使用一条规则来将所有的.c文件编译为对应的.o文件。
模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值
1 | |
Makefile自动化变量
自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中
| 自动化变量 | 描述 |
|---|---|
| $@ | 规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。 |
| $% | 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。 |
| $< | 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。 |
| $? | 所有比目标新的依赖目标集合,以空格分开。 |
| $^ | 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。 |
| $+ | 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。 |
| $* | 这个变量表示目标模式中”%”及其之前的部分,如果目标是test/a.test.c,目标模式为a.%.c,那么“$*”就是test/a.test。 |
这里使用$<
1 | |
Makefile伪目标
Makefile有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行make命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。使用伪目标的主要是为了避免Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的,比如我们这里的clean规则
1 | |
上述规则中并没有创建文件clean的命令,因此工作目录下永远都不会存在文件clean,
当我们输入“make clean”后,“rm *.o”和“rm main”总是会执行。
如果在工作目录下创建一个名为“clean”的文件时,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的rm命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。
为了避免这个问题,我们可以将clean声明为伪目标,声明方式如下:
1 | |
1 | |

Makefile条件判断
1 | |
else与<条件为假时执行的语句>可省略条件为假时执行的语句>
| 条件关键字 | 描述 |
|---|---|
| ifeq | 判断是否相等 |
| ifneq | 判断是否不相等 |
| ifdef<变量名>变量名> | 如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。 |
| ifndef<变量名>变量名> | 如果“变量名”的值为空,那么表示表达式为真,否则表达式为假。 |
常见Makefile函数使用
Makefile支持函数,类似C语言一样,Makefile中的函数是已经定义好的,直接使用,不支持自定义函数。
1 | |
1 | |
调用函数和调用普通变量一样,使用符号$来标识。
参数集合是函数的多个参数,参数之间以逗号“,”隔开,
函数名和参数之间以“空格”分隔开,函数的调用以$开头。
subst
字符串替换
1 | |
此函数的功能是将字符串
patsubst
模式字符串替换
1 | |
此函数查找字符串
dir
获取目录
1 | |
此函数用来从文件名序列
notdir
提取文件名
1 | |
此函数用与从文件名序列
foreach
循环
1 | |
此函数的意思就是把参数中的单词逐一取出来放到参数中,然后再执行
wildcard
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数wildcard
1 | |
1 | |