1.背景知识
- 预处理(进行宏替换,这一步会生成 .i 后缀的文件)
- 编译(生成汇编,会生成 .s 后缀的文件)
- 汇编(生成机器可识别代码,生成 .o 后缀文件)
- 链接 (生成可执行文件或库文件)
2.gcc如何完成
格式
gcc [选项] 要编译的文件 [选项] [目标文件]
举例:由于编译的是c++代码,所以使用的是g++,其他跟gcc一样
预处理(进行宏替换)
- 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
- 预处理指令是以#开头的代码行。
- 实例:
gcc -E hello.c -o hello.i- 选项“-E”,作用是让gcc在预处理结束后停止编译过程
- 选项“-o”,是指目标文件,“i”文件为已经过预处理的C原始程序。
编译(生成汇编)
- 在这个阶段中,gcc首先要检查代码的规范性,是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
- 用户可以使用“-S”选项来进行查看,该选项只能进行编译而不进行汇编,生成汇编代码。
- 实例:
gcc -S hello.i -o hello.s
汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的“.s”文件转换成目标文件。
- 可以使用选项“.c”就可以看到汇编代码转换为“.o”的二进制目标代码了。
- 实例:
gcc -c hello.s -o hello.o

用vim打开会有一堆乱码:
我们可以使用二进制查看工具 od 进行查看(以8进制或16进制去显示):
链接(生成可执行文件或库文件)
- 在编译之后就进入了链接阶段。
- 实例:
gcc hello.o -o hello

3.函数库
在上面的C程序中并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,但是系统把这些函数实现都被放入到名为 libc.so.6 的库文件中去了,在没有特别指定时 gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能间接实现函数“printf”了,而这也就是链接的作用
- 函数库一般分为 静态库 和 动态库 两种
- 静态库
指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为“.a”
- 动态库
在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销,一般后缀名为“.so”
注: gcc 在编译时默认使用动态库,可以通过 file 命令查看文件信息

- 总结:
- 以动态库链接方式生成的可执行程序的体积相对比较小,可以节省系统空间资源,缺点是一旦库失效,依赖此库的程序就无法运行。
- 以静态库链接方式生成的可执行程序体积相对较大,因为它会将库中的代码拷贝到可执行程序,缺点就是程序体积大,浪费系统空间资源,但是如果库失效则不影响程序的运行。
- 静态链接的时候,需要 .a静态库
- 动态库链接的时候,需要 .so动态库
- gcc选项总结:
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面 -S 编译到汇编语言不进行汇编和链接 -c 编译到目标代码 -o 文件输出到 文件 -static 此选项对生成的文件采用静态链接 -g 生成调试信息。GNU 调试器可利用该信息 -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库 -O0 -O1 -O2 -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 -w 不生成任何警告信息 -Wall 生成所有警告信息(默认生成)


