一、代码编译
- 没有使用VIM编辑器,使用VSCode编辑代码,简易容易上手,且支持Linux。
1、 编译代码
(1)安装编译需要的软件包
- 安装 gcc、g++、make 等工具,build-essential提供了编译程序所需的所有软件包。
sudo apt-get install build-essential
- 查看GCC版本号,“Target: x86_64-linux-gnu”一行,这说明这个 GCC 编译器是针对 X86 架构的,因此只能编译在 X86 架构 CPU 上运行的程序。如果想要编译在 ARM 上运行的程序就需要针对 ARM 的 GCC 编译器,也就是交叉编译器! ARM 开发,因此后期肯定要安装针对 ARM 架构的 GCC 交叉编译器。目标架构不同,需要的GCC编译器就不同。
gcc -v
(2)编译代码命令
- GCC 编译器和其它编译器一样,不仅能够检测出错误类型,而且标记除了错误
发生在哪个文件,哪一行。
gcc [选项] [文件名字]
-c:只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件。
-o:<输出文件名>用来指定编译结束以后的输出文件名,如果使用这个选项的话 GCC 默认编译出来的可执行文件名字为 a.out。 -g:添加调试信息,如果要使用调试工具(如 GDB)的话就必须加入此选项,此选项指示编
译的时候生成调试所需的符号信息。
-O:对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的的时候都会进行优化,这样产生的可执行文件执行效率就高。
-O2:比-O 更幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢。
- VScode编辑代码
- 终端编译代码指令gcc,./a.out运行代码
//默认编译后输出a.out
gcc main.c
//指定输出文件名称,输出main
gcc main.c –o main
(3)编译流程
- GCC 编译器的编译流程是:预处理、编译、汇编和链接。
- 预处理就是展开所有的头文件、替换程序中的宏、解析条件编译并添加到文件中。
- 编译是将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译。
- 汇编就是将汇编语言文件编译成二进制目标文件。
- 链接就是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉及到静态库和动态库等问题。
二、Makefile基础
1、简介
- 像脚本文件一样,一键编译多个文件。在工程目录下创建名为“Makefile”的文件,
文件名一定要叫做“Makefile”!!!
作用:
1、如果工程没有编译过,那么工程中的所有.c 文件都要被编译并且链接成可执行程序。
2、如果工程中只有个别 C 文件被修改了,那么只编译这些被修改的 C 文件即可。
3、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的 C 文件,并且
链接成可执行文件。
- 编辑Makefile,所有行首需要空出来的地方一定要使用“TAB”键!不要使用空格键!
gcc -o main main.o input.o calcu.o //将编译出来的所有.o 文件链接成可执行文件 main
//分别是将 main.c、input.c 和 calcu.c 编译成对应的.o 文件,使用了“- c”选项,只编译不链接
gcc -c main.c
gcc -c input.c
gcc -c calcu.c
- Makefile 编写好以后我们就可以使用 make 命令来编译我们的工程了,直接在命令行中输入“make”即可,make 命令会在当前目录下查找是否存在“Makefile”这个文件,如果存在的话就会按照 Makefile 里面定义的编译方式进行编译。
- Makefile 中命令缩进没有使用 TAB 键,会导致一下错误。
2、Makefile 语法
(1)Makefile 规则格式
目标…... : 依赖文件集合……
命令 1
命令 2
……
- 举例,这条规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件,如果要更新目标 main,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍规则中的命令列表。规则的命令是用来创建或者更新目标的。规则除第一条,没有先后顺序。
//这是一条规则
main : main.o input.o calcu.o //目标:依赖文件集合
gcc -o main main.o input.o calcu.o //命令1
(2)Make 的执行过程
1、make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件。
2、当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话就会执行后面的命令来更新目标。
(3)Makefile 变量
- Makefile 中的变量都是字符串!类似 C 语言中的宏。
- 举例,main.o input.o 和 calcue.o输入了两遍
main: main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
可替换为:
#Makefile 变量的使用 //注释
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
- ① 赋值符“=”
- 在 Makefile 要输出一串字符的话使用“echo”,就和 C 语言中的“printf”一样。
- 赋值符“=”,变量的真实值取决于它所引用的变量的最后一次有效值。
name = zzk
curname = $(name)
name = david
print:
@echo curname: $(curname)
结果:curname:david
- ② 赋值符“:=”
- 赋值符“:=”,变量的真实值取决于它所引用的变量的第一次定义值。
- ③ 赋值符“?=”
- 如果前面curname没有被赋值,就使用?=这个变量值。
curname ?= david
- ④变量追加“+=”
- main.o input.o calcu.o
objects = main.o inpiut.o
objects += calcu.o
(4)Makefile 模式规则
- “%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
- 当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方
法如下:
%.o : %.c
命令
main: main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
clean:
rm *.o
rm main
使用模式规则改为:
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
%.o : %.c
#命令
clean:
rm *.o
rm main
(5)Makefile自动化变量 ?
- 常用的三种:
$@:规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$<:依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。
$^:所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。
- $@
- $<
- $^
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
%.o : %.c #使用了模式规则
gcc -c $< #使用了自动化变量
clean:
rm *.o
rm main
(5)Makefile 伪目标
- 使用伪目标的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文
件出现名字冲突,也可以编写一个规则用来执行一些命令。
.PHONY : clean
- 声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
.PHONY : clean #伪目标
%.o : %.c #使用了模式规则
gcc -c $< #使用了自动化变量
clean:
rm *.o
rm main
(6)Makefile 条件判断
-
关键字:ifeq、ifneq、ifdef 和 ifndef
-
语法一:
<条件关键字>
<条件为真时执行的语句>
endif
- 语法二:
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
- ① ifeq、ifneq 判断是否相等
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”
- ② ifdef、ifndef 判断是否非空
- 如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。“变量名”同样可以是一个函数的返回值。
ifdef <变量名>
(7)Makefile 函数使用(不支持自定义函数)
$(函数名 参数集合)
或者
${函数名 参数集合}
- ① 函数 subst
- 把字符串“my name is zzk”中的“zzk”替换为“ZZK”
$(subst zzk,ZZK,my name is zzk)
- ②函数 patsubst
- 将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字
符串为“a.o b.o c.o”。
$(patsubst %.c,%.o,a.c b.c c.c)
- ③函数 dir
- 提取文件“/src/a.c”的目录部分,也就是“/src”
$(dir </src/a.c>)
- ④函数 notdir
- 提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
$(notdir </src/a.c>)
- ⑤函数 foreach
- 此函数的意思就是把参数中的单词逐一取出来放到参数中,然后再执行
所包含的表达式。每次 都会返回一个字符串,循环的过程中, 中所包含的每个字符串会以空格隔开,最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
- ⑥函数 wildcard
- 获取当前目录下所有的.c 文件,类似“%”。
$(wildcard *.c)
- 参照【正点原子】文档学习。