简介
在Linux的系统中,想要完成代码编译,gcc/g++是不可缺少的工具,而make/Makefile能否熟练应用则从一个侧面体现出一个人是否有能力独自完成一个大型工程,而本篇文章就带领大家了解一些gcc/g++和make/Makefile使用的基础知识。
一、编辑器gcc/g++使用
1.1 背景知识
GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如 Linux、 BSD、MacOS X等)采纳为标准的编译器,甚至在微软的Windows上也可以使用GCC。GCC支持多种 计算机体系结构芯片,如 x86、 ARM、MIPS等,并已被移植到其他多种硬件平台[1]
PS:gcc适用于C语言编译,g++适用于c++和c,两者的语法完全相同,
1.2 gcc的完成过程
格式:[gcc [选项] 要编译的文件 [选项] [目标文件]]
1.2.1 预处理阶段(进行宏替换)
作用:包括 处理宏定义,文件包含展开,条件编译,去注释等等
命令: -E ,意思是编译到预处理阶段停止,预处理阶段的文件我们一般以 .i为后缀命名
格式:[gcc -E [要编译的文件] -o [编译后的名字] ]
![](https://i-blog.csdnimg.cn/blog_migrate/a32ebfd98421799846bc5546200198f4.png)
如图所示,左边是经过预处理后的源代码,我们发现宏定义都已经带入,而左边代码八百多行,是引用的stdio.h文件已经展开,而注释以及去除,条件编译也只留下了符合条件的。
1.2.2 编译阶段(生成汇编)
作用:在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成 汇编语言。
命令:-S,文件编译到编译阶段停止,编译阶段的文件我们一般以 .s为后缀
格式:[gcc -S [要编译的文件] -o [编译后的名字]]
![](https://i-blog.csdnimg.cn/blog_migrate/b12bba688d257b668b7cf6ee0be9672a.png)
如图左边就是编译阶段的文件,如图所示已经转为汇编语言。
1.2.3 汇编阶段(生成机器可以识别的代码)
作用:汇编阶段是把编译阶段生成的“.s”文件转成目标文件
命令:-c,将文件编译到汇编阶段停止,一般以.o为后缀
格式:[gcc -c [要编译的文件] -o [编译后文件的名字]]
![](https://i-blog.csdnimg.cn/blog_migrate/788cdb5c782178ad30c64161c422ac68.png)
如图所示,左边就是经过汇编变为机器可以读取的二进制代码
1.2.4 链接阶段(生成可执行文件和库文件)
作用:在成功编译后进入链接阶段,将程序和函数库链接起来,形成可执行文件和库文件
命令:直接gcc即可,一般不许要后缀
格式:[gcc [要编译的文件] -o [编译后的名字]]
![](https://i-blog.csdnimg.cn/blog_migrate/44b1c03f7a76900141fe344d2c3144cb.png)
如图,亮绿光的那个文件就是链接后的可执行文件,执行后即可实现代码。
1.3 函数库
在链接阶段,我们涉及到了一个关键概念——函数库。
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函
数“printf”了,而这也就是链接的作用。
而函数库一般分为动态库(专门让编译器对用户进行动态链接)和静态库(专门让编译器对用户进行静态链接)
1.3.1 动态库
动态库的链接指的是 拷贝动态库中我所需要的函数的地址,将其放在可执行程序中的相关位置,但是完成后 可执行文件需要依赖动态库才可以执行,Linux下,默认执行的都是动态链接。我们可以通过[file]命令来查看文件属于什么链接。
![](https://i-blog.csdnimg.cn/blog_migrate/f25fa8cb0cf9418e7861c6a3aea951fa.png)
如图所示,Linux下默认执行的是动态链接
1.3.2 静态库
静态库的链接是指 拷贝静态库中的函数,将其放在可执行程序中, 完成后不需要依赖动态库,自己就可以完成程序运行。
由于Linux下默认是动态链接,想要实现静态链接需要用特殊命令:-static
格式:[gcc [要编译的文件] -o [编译后的名字] -static]
![](https://i-blog.csdnimg.cn/blog_migrate/daf411cbd9f062082682dedaa6c0e96a.png)
如图,test.static文件就是静态链接后的可执行文件
1.3.3 动静态库的区别
静态库: 虽然不再依赖动态库, 但是由于函数全部拷贝的原因,比较消耗空间。
动态库: 可以被大家共享使用,其真正的实现永远在库里,程序内部只有地址,比较节省空间, 但是需要依赖动态库
![](https://i-blog.csdnimg.cn/blog_migrate/95efd5f428341431501701fd1a40cdd5.png)
如图所示,我们发现动静态链接的空间消耗差距巨大。
1.4 gcc常见选项
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S 编译到汇编语言不进行汇编和链接
-c 编译到目标代码
-o 文件输出到 文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息。GNU 调试器可利用该信息。
-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-O0
-O1
-O2
-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
二、项目自动化构建工具make/Makefile的使用
2.1 背景
1.会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
2.一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
3.makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
4.make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
5.make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建
2.2 make/Makefile的运行
想了解Makefile的运行我们可以先举一个例子
![](https://i-blog.csdnimg.cn/blog_migrate/dd1e4164b0fb99b9b137d0b3b0d57250.png)
如图我们先创建一个Makefile文件,写入如上内容
![](https://i-blog.csdnimg.cn/blog_migrate/87bdde8c1ba7e631d0f924605459f705.png)
当我们第一次输入make时,会默认执行Makefile的第一个目标文件,也就是编译test.c,形成可执行程序test。想要执行Makefile的下一个目标文件,就需要设定关键字,我们这里想要执行的是删除test,所以我们设定的关键字为clean
2.2.1 依赖关系和依赖方法
make/Makefile的运行离不开 依赖关系和依赖方法,那什么依赖关系和依赖方法呢?
![](https://i-blog.csdnimg.cn/blog_migrate/d5bae12bee360be329ab80727ed9c019.png)
以上面的例子为例
1. test需要有test.c才可以实现,如图,第一行就是依赖关系
2. 而从test.c编译成test的方法就是依赖方法,如图,第二行就是依赖方法。
2.2.2 原理
make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,
1. make会在 当前目录下找名字叫“ Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的 第一个目标文件(target),在上面的例子中,他会找到“test”这个文件,并把这个文件作为最终的目标文件。
3. 如果test文件 不存在,或是test所依赖的后面的test.c文件的 文件修改时间要比test这个文件新(可以用 touch 测试),那么, 他就会执行后面所定义的命令来生成test这个文件。
4. 如果test所依赖的test.c文件不存在,那么make会在当前文件中找目标为test.c文件的依赖性,如果找到则再根据那一个规则生成test.c文件。(这有点像一个堆栈的过程)
5. 当然,你的C文件和H文件是存在的啦,于是make会生成 test.c 文件,然后再用 test.c 文件声明make的终极任务,也就是执行文件test了。
6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
2.2.3 项目清理
1.工程是需要被清理的
2.像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
3.但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
总结
以上就是本篇文章的全部内容,希望铁子们能够有所收获。