1.背景知识

  1. 预处理(进行宏替换,这一步会生成 .i 后缀的文件)
  2. 编译(生成汇编,会生成 .s 后缀的文件)
  3. 汇编(生成机器可识别代码,生成 .o 后缀文件)
  4. 链接 (生成可执行文件或库文件)

2.gcc如何完成

格式 gcc [选项] 要编译的文件 [选项] [目标文件]

举例:由于编译的是c++代码,所以使用的是g++,其他跟gcc一样
image.png

预处理(进行宏替换)

  • 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
  • 预处理指令是以#开头的代码行。
  • 实例:gcc -E hello.c -o hello.i
  • 选项“-E”,作用是让gcc在预处理结束后停止编译过程
  • 选项“-o”,是指目标文件,“i”文件为已经过预处理的C原始程序。

image.pngimage.png

编译(生成汇编)

  • 在这个阶段中,gcc首先要检查代码的规范性,是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
  • 用户可以使用“-S”选项来进行查看,该选项只能进行编译而不进行汇编,生成汇编代码。
  • 实例:gcc -S hello.i -o hello.s

image.png
image.png

汇编(生成机器可识别代码)

  • 汇编阶段是把编译阶段生成的“.s”文件转换成目标文件。
  • 可以使用选项“.c”就可以看到汇编代码转换为“.o”的二进制目标代码了。
  • 实例:gcc -c hello.s -o hello.o

image.png
用vim打开会有一堆乱码:
image.png
我们可以使用二进制查看工具 od 进行查看(以8进制或16进制去显示):
image.png

链接(生成可执行文件或库文件)

  • 在编译之后就进入了链接阶段。
  • 实例:gcc hello.o -o hello

image.png

3.函数库

在上面的C程序中并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,但是系统把这些函数实现都被放入到名为 libc.so.6 的库文件中去了,在没有特别指定时 gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能间接实现函数“printf”了,而这也就是链接的作用

  • 函数库一般分为 静态库 和 动态库 两种
  1. 静态库

    指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为“.a”

  1. 动态库

    在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销,一般后缀名为“.so”

注: gcc 在编译时默认使用动态库,可以通过 file 命令查看文件信息

image.png

  • 总结:
    1. 以动态库链接方式生成的可执行程序的体积相对比较小,可以节省系统空间资源,缺点是一旦库失效,依赖此库的程序就无法运行。
    2. 以静态库链接方式生成的可执行程序体积相对较大,因为它会将库中的代码拷贝到可执行程序,缺点就是程序体积大,浪费系统空间资源,但是如果库失效则不影响程序的运行。
    3. 静态链接的时候,需要 .a静态库
    4. 动态库链接的时候,需要 .so动态库
  • gcc选项总结:

    -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面 -S 编译到汇编语言不进行汇编和链接 -c 编译到目标代码 -o 文件输出到 文件 -static 此选项对生成的文件采用静态链接 -g 生成调试信息。GNU 调试器可利用该信息 -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库 -O0 -O1 -O2 -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 -w 不生成任何警告信息 -Wall 生成所有警告信息(默认生成)