gcc是C语言对应的编译器,g++是C++对应的编译器。一段代码要转换成机器可以识别的形式,编译器是必不可少的;要让代码可以找到自己所需要的函数或者变量,这就需要链接器了,最终才生成我们看到的 可执行文件。这整个过程我们称为 “程序的编译过程” 。
目录
一、预处理
1、预处理的作用
预处理的主要功能是头文件包含、宏替换、条件编译、去除注释等,生成一个比较干净的C原始程序
- 头文件展开:将#include 包含的头文件在目标文件展开
- 宏替换:将定义的宏 替换为对应的值
- 条件编译:遇到 #ifdefine 就进行条件判断,然后仅留下相应的结果,生成的文件中就不会包 含#ifdefine等语句
- 去除注释:既然要生成一个干净的C程序,注释是必然不需要的
2、 预处理指令
基本指令:gcc -E 依赖的.c文件 -o 输出的.i文件
#使用gcc编译器,编译的文件是add.c,执行到预处理就停止编译,生成的目标文件是add.i文件
gcc -E add.c -o add.i
-E:执行完预处理就停止编译过程
-o:目标文件,预处理生成的是 .i文件
二、编译(生成汇编代码)
1、编译的作用
这个阶段的主要任务是
- 语法分析
- 词法分析
- 语义分析
- 符号汇总(汇总的一般都是全局符号)
没有问题以后,将代码转化成汇编代码。
注意:这里汇总的目的是为后面两步在作准备。下一步的汇编阶段会生成符号表,链接阶段会合并符号表,实际程序在运行的时候,就会根据这个符号表去找对应的全局变量或者函数。至于为什么只汇总全局符号,因为全局内容可以被不同文件共享。
2、 编译指令
基本指令:gcc -S 依赖的.c文件 -o 生成的.s文件
gcc -S 依赖的.i文件 -o 生成的.s文件
方式一:依赖.c文件。从头开始,经过预处理、编译,最后生成.s文件
gcc -S add.c -o add.s
方式二:依赖.i文件。使用上一次生成的文件,无需经过预处理,直接对上一次的文件进行编译
gcc -S add.i -o add.s
-S:执行完编译就停止
-o:生成的目标文件是 .s 文件
三、汇编(生成机器可识别代码)
1、汇编的作用
汇编的作用主要有两点:
- 生成符号表。我们在调用其他源文件中的函数时,比如Add函数,实际上就是根据最后合并的符号表中的地址来找到对应的函数。一个源文件就对应一个符号表。
- 计算机无法识别汇编语言,需要生成计算机可识别代码(也就是二进制文件)
2、为什么不直接将c语言代码转化为二进制文件
第一个原因,早期写代码都是用的汇编语言,后来有了C语言,就有了现在的流程,C语言 ——》汇编语言 ——》二进制文件,分别对应着我们目前的三个阶段预处理 ——》 编译 ——》 汇编
第二个原因,现在如果要将c语言直接转化为 二进制文件,就相当于要放弃现有的编译器,成本太高。
3、 汇编指令
基本指令:gcc -c 依赖的.c文件 -o 生成的.o文件
gcc -c 依赖的.s文件 -o 生成的.o文件
(注意这里的-c 是小写的!!)
方式一:依赖.c文件。从头开始,经过预处理、编译、汇编,最后生成.o文件
gcc -c add.c -o add.o
方式二:依赖.s文件。使用上一次生成的文件,无需经过预处理、编译,直接对上一次的文件进行汇编操作
gcc -c add.s -o add.o
-c:执行完汇编就停下来
-o:生成的目标文件是 .o文件
四、链接(生成可执行文件或库文件)
1、链接的作用
首先会把上面不同源文件的 符号表合并;其次开始解读代码,若要用到xx第三方函数,就会根据路径连接到对应的库。在该阶段,Windows环境连接的是 .lib 文件来访问第三方库;Linux 环境连接的是 .so 文件来访问第三方库。
这一步会将生成的 .o文件转化为 可执行文件(Windows下的.exe文件)
2、连接指令
基本指令:gcc 依赖的.o文件 -o 生成的可执行文件
gcc -o 生成的可执行文件 依赖的.c文件列表
方式一:从头开始,一直运行到最后生成可执行文件
gcc -o add add.c
方式二:在上一次add.o的基础上运行
gcc add.o -o add
这里就一直运行到结束了,无需停下来了
-o:生成的目标文件是可执行文件
五、整体过程
整体过程可以参考下图:
- 将源文件通过预处理、编译、汇编转换成目标文件(xxx.o文件)
- 目标文件由链接器(linker)捆绑在一起,同时从链接库中引入该程序所需要用到的函数(可以是标准C函数库、自定义链接库),最终形成一个单一而完整的可执行程序。
注意:不同环境下,链接和运行过程链接第三方库的方式可能不一样
- Windows环境下
- 链接阶段 —— 连接.lib 文件
- 运行阶段 —— 连接 .dll 文件
- Linux 环境下,无论是链接过程还是运行过程,用的都是 .so 文件。