gcc编译器、Makefile基础、功能、模式规则、自动化变量

本文详细介绍了GCC编译器的使用,包括编译选项如-c、-o、-g和-O等,以及编译流程:预处理、编译、汇编和链接。此外,还探讨了Makefile在大型项目中的重要性,如何编写Makefile规则以实现自动化编译,并讲解了Makefile中的变量、模式规则和自动化变量。Makefile中的函数如 subst、patsubst、dir 和 notdir 等也被提及。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gcc编译器

gcc [选项] [文件名字]
-c: 只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件。
-o: <输出文件名>用来指定编译结束以后的输出文件名,如果不使用这个选项的话 GCC
编译出来的可执行文件名字为 a.out
-g: 添加调试信息,如果要使用调试工具(如 GDB)的话就必须加入此选项,此选项指示编
译的时候生成调试所需的符号信息。
-O: 对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的的时候都会进
行优化,这样产生的可执行文件执行效率就高。
-O2: 比-O 更幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢。

GCC 编译器的编译流程是:预处理、编译、汇编和链接。

预处理就是展开所有的头文件、替换程序中的宏、解析条件编译并添加到文件中(进行宏替换,去掉注释、引入头文件)。

编译是将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译(将高级语言代码解释成为汇编指令,进行语法语义纠错)。

汇编就是将汇编语言文件编译成二进制目标文件(将汇编指令解释为机器指令)。

链接就是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉及到静态库和动态库等问题(将我们的机器指令与所使用的的库函数对应库文件中的机器指令打包到一起组织成为可执行程序)。

预处理:gcc -E main.c -o main.i

编译:gcc  -S main.i -o main.s

汇编:gcc  -c main.s -o main.o (注意是小写c) 

链接:gcc main.o -o main

Makefile基础

如果我们的工程只有一两个 C 文件还好,需要输入的命令不多,当文件有几十、上百甚至上万个的时候用终端输入 GCC 命令的方法显然是不现实的。如果我们能够编写一个文件,这个文件描述了编译哪些源码文件、如何编译那就好了,每次需要编译工程的时只需要使用这个文件就行了。这种问题怎么可能难倒聪明的程序员,为此提出了一个解决大工程编译的工具: make,描述哪些文件需要编译、哪些需要重新编译的文件就叫做 Makefile,Makefile 就跟脚本文件一样, Makefile里面还可以执行系统命令。使用的时候只需要一个 make命令即可完成整个工程的自动编译。
 

Makefile编写规则:

目标对象:依赖对象

[tab]要执行的指令

想想若要编译几万个文件编译一次所需要的时间就可怕。最好的办法肯定是哪个文件被修改了,只编译这个被修改的文件即可,其它没有修改的文件就不需要再次重新编译了,为此我们改变我们的编译方法,如果第一次编译工程,我们先将工程中的文件都编译一遍,然后后面修改了哪个文件就编译哪个文件。

Makefile功能:

1、如果工程没有编译过,那么工程中的所有.c 文件都要被编译并且链接成可执行程序。
2、如果工程中只有个别 C 文件被修改了,那么只编译这些被修改的 C 文件即可。
3、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的 C 文件,并且链接成可执行文件。
eg:

 总结一下 Make 的执行过程:
1、 make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件。
2、当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比
目标文件晚)的话就会执行后面的命令来更新目标。
这就是 make 的执行过程, make 工具就是在 Makefile 中一层一层的查找依赖关系,并执行相应的命令。编译出最终的可执行文件。Makefile的好处就是“自动化编译”,一旦写好了Makefile文件,以后只需要一个 make 命令即可完成整个工程的编译,极大的提高了开发效率。

     Makefile 中变量的引用方法是“$(变量名),在 Makefile 要输出一串字符的话使用“echo”,就和 C 语言中的“printf”一样,第 6 行中的“echo”前面加了个“@”符号,因为 Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“@”的话就不会输出命令执行过程,例如:

注意=、:=、?=

一 、=含义:借助另外一个变量,可以将变量的真实值推到后面去定义。也就是变量的真实值取决于它所引用的变量的最后一次有效值

 二、 :=不会使用后面定义的变量,只能使用前面已经定义好的

 三、?=含义是:如果变量前面没有被赋值,那么此变量就用当前值,如果前面已经赋过值了,那么就使用前面赋的值。

四、变量追加“+=”,Makefile 中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=”。

Makefile模式规则及自动化变量: 

a.%.c 就表示以 a.开头,以.c 结束的所有文件

当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方法如下:
%.o:%.c:

        命令

所以Makefile可改为以下形式:

 自动化变量如下:

自动化变量描述
$@规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模
式中定义的目标集合。
$%当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,
那么其值为空。
$<依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%” )定义的,那么
“$<”就是符合模式的一系列的文件集合。
$?所有比目标新的依赖目标集合,以空格分开。
$^所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,
“$^”会去除重复的依赖文件,值保留一份。
$+和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
$*这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模
式为 a.%.c,那么“$*”就是 test/a.test。

 Makefile 伪目标

      Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代
表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命
令。
      使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件
现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文
件的,如:

clean:
        rm *.o
        rm main

     上述规则中并没有创建文件 clean 的命令,因此工作目录下永远都不会存在文件 clean,我们输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。可是如果在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。为了避免这个问题,我们可以将 clean 声明为伪目标,声明方式如下:

.PHONY : clean

eg:

     

Makefile函数使用:

       Makefile 支持函数,类似 C 语言一样, Makefile 中的函数是已经定义好的,我们直接使用,
不支持我们自定义函数。 make 所支持的函数不多,但是绝对够我们使用了,函数的用法如下:
$(函数名 参数集合)
或者:
${函数名 参数集合}
可以看出,调用函数和调用普通变量一样,使用符号“$”来标识。参数集合是函数的多个参数,参数之间以逗号“,”隔开,函数名和参数之间以“空格”分隔开,函数的调用以“$”开头。接下来介绍几个常用的函数:
1、函数 subst
函数 subst 用来完成字符串替换,调用形式如下:
$(subst <from>,<to>,<text>)
此函数的功能是将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符
串,比如如下示例:
$(subst zzk,ZZK,my name is zzk)

把字符串“my name is zzk”中的“zzk”替换为“ZZK”,替换完成以后的字符串为“my name is ZZK”
2、函数 patsubst
函数 patsubst 用来完成模式字符串替换,使用方法如下:
$(patsubst <pattern>,<replacement>,<text>)
此函数查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉, <pattern>可以使用通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串,比如:
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
3、函数 dir
函数 dir 用来获取目录,使用方法如下:
$(dir <names…>)
此函数用来从文件名序列<names>中提取出目录部分,返回值是文件名序列<names>的目录部分,比如:
$(dir </src/a.c>)
提取文件“/src/a.c”的目录部分,也就是“/src”。
4、函数 notdir
函数 notdir 看名字就是知道去除文件中的目录部分,也就是提取文件名,用法如下:
$(notdir <names…>)
此函数用与从文件名序列<names>中提取出文件名非目录部分,比如:
$(notdir </src/a.c>)
提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
5、函数 foreach
foreach 函数用来完成循环,用法如下:
$(foreach <var>, <list>,<text>)
此函数的意思就是把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中, <text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时, <text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
6、函数 wildcard
       通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard,使用方法如下:
$(wildcard PATTERN…)
比如:
$(wildcard *.c)
上面的代码是用来获取当前目录下所有的.c 文件,类似“%”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值