Linux ———— 编译器g++/gcc
一、背景知识
(1)预处理(进行宏替换/去注释/条件编译/头文件展示等)
(2)编译(生成汇编)
(3)汇编(生成机器可识别代码)
(4)链接(生成可执行文件或库文件)
二、gcc编译选项
格式 :gcc [选项] 要编译的文件 [选项] [目标文件]
1、预处理
预处理功能主要包括宏定义,文件包含,条件编译,去注释等
预处理指令是以#号开头的代码行
实例 : gcc - E hello.c -o hello.i
jie.i jie.c
选项-E,该选项的作用是让gcc在预处理结束后停止编译过程
选项 -o 是指目标文件, .i文件为已经预处理的C原始程序
补充:
- 在 gcc 编译命令中, -o 是指目标文件 。它用于指定编译后生成的输出文件名称,可自定义输出文件名及路径,方便将源文件编译结果保存到期望位置 。比如图中 gcc -E hello.c -o hello.i ,就是将预处理后的结果输出到名为 hello.i 的文件中。
- 如果在 gcc 命令中不使用 -o 选项指定目标文件:
- 一般情况 : 对于简单的编译,如 gcc hello.c ,在没有指定 -o 时, gcc 默认会在当前目录生成一个名为 a.out 的可执行文件 。例如,若 hello.c 是一个简单的C语言源文件,包含 main 函数,执行 gcc hello.c 后,当前目录下就会出现 a.out ,可以通过 ./a.out 来运行它。
- 特定编译阶段 :当使用像 -E (预处理后停止)、 -S (编译后停止,生成汇编代码)、 -c (汇编前停止,生成目标文件) 这些选项时,如果不指定 -o ,不同编译器行为可能有差异,但通常不会自动生成有意义文件名的输出文件,可能导致编译结果无法正确保存和使用 。比如 gcc -E hello.c ,没有 -o 选项指定输出,就不会得到一个保存预处理结果的文件,也就难以获取和查看预处理后的内容。所以,为了准确保存各编译阶段结果或最终可执行文件,通常要使用 -o 选项来明确指定目标文件名。
2、编译(生成汇编)
- 在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,检查无误之后,gcc把代码翻译成汇编语言。
- 用户可以使用-S选项来进行查看,该选项只能进行汇编,生成汇编代码
- 实例:gcc -S hello.i - o hello.s
3、汇编(生成机器可执行代码)
- 汇编阶段是把编译阶段生成的.s文件转成目标文件
- 读者在此可使用选项 “-c”就可以看到汇编代码转化为“.o”的二进制目标代码了
- 实例:gcc - c hello.s -o hello.o
4、链接(生成可执行代码)
- 在成功编译之后,就进入链接阶段
- 实例:gcc hello .o -o hello
三、动态链接和静态链接
1、静态链接
- 在我们实际开发中,不可能将所有的代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之前不是独立的,而会存在依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每一个文件都是单独编译的,即每一个.c文件都会形成一个.o文件。为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接
- 缺点:浪费空间 ,更新比较困难
2、动态链接
- 动态链接的出现解决了静态链接中提到的问题。动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将她们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行程序
3、库
- 我们的c程序中,并没有定义printf的函数的实现,且在预编译中包含的stdio.h中也只有该函数的声明,而没有定义函数的实现,是因为系统把这些函数的实现都被放到libc.so.6的库文件中了,在没有特别指定时,gcc会到系统默认的搜索路径/usr/lib下进行查找,也就是连接到libc.so.6库函数中去,这样就可以实现printf
四、自动化构造-make/Makefile
1、背景
- 一个工程中的源文件很多,其按类型、功能、模块分别放在了很多的目录中,makefile定义了一系列的规则来指定,那些文件需要先编译,那些文件需要后编译或者其他更复杂的功能操作
- makefile带来的好处就是——自动化编译,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高软件开发的效率
- make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构造
- 如果我们写两个,我们会发现他始终会执行第一个,那是因为Linux默认匹配的第一个,如果我们想执行第二个我们需要make 名称
- 我们当我们执行一次命令以后我们如果再一次的执行就不行了,那是因为文件的属性中其实有三个时间:Modify : 内容变更,时间更新,change:属性变更,时间变更。Access: 经常指文件最近被访问的时间。每一次make都会对比源文件和可执行目标文件的的Modify,如果源文件的时间比可执行目标文件的Modify时间晚就执行,反之不执行
- 当我们修改源文件的时间就可以实现
2、 伪目标
.PHONY:让make忽略源文件和可执行文件的时间对比,被他修饰的文件叫做伪目标,一般用于像clean这种需要总是被执行的文件
五、
我们以后最主要:将所有的文件成.o,在统一链接
make工作原理
1、make会在当前找到名字叫做Makefile或者makefile的文件
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到code文件,并把这个文件作为最终的目标文件
3、如果code文件不存在,或者code所依赖的后面的code.o文件的文件修改时间要比code新,那么他会执行后面所定义的命令来生成code文件
4、如果code所依赖的code.o文件找不到,那么他会在当前文件种找目标为code.o的依赖关系,如果找到在按规矩生成 code.o....以此类推(有一像一个建栈的过程)
5、这就是整个make的依赖关系,make会一层一层的找文件的依赖关系,直到最终编译出第一个 目标文件
6、在找寻的过程中,如果出现错误,比如最后的依赖文件找不到了,那么make就会直接退出, 并报错,而对于所定义的命令的错误,或是编译不成功,make不会理
7、make只管文件的依赖关系,如果在我找到依赖关系后,冒号后面的文件还是没有,make不会 接着寻找
五、扩展语法
1、可以定义变量
@命令不回显
新的写法
这样写就类似于宏,可以方便我们修改
4、$@: 代表目标文件名。$^:代表依赖关系列表
但是上述并没有体现我们 所说需要把所有的文件变成。o,在链接到一起,常见写法
5、wildcard*.c
获取当前所有.c文件名
6、SRC:.c=.o
将和SRC的同名.c替换形成目标文件列表
7、%.c
展开当前目录下所有的.c,
8、%.o
同时展开当前目录下所有.o
9、<
对展开的依赖.c文件,一个一个的交给gcc