gcc编译器的C语言
今天用一篇文章告诉你gcc编译器眼中的C语言是什么样的。别眨眼,开始
编程语言一般分为编译期、运行期。有的编程语言是编译期与运行期合并在一起的,比如Python、PHP…即解释型语言。有的则是编译期与运行期分开的,比如C、C++、Java…即编译型语言
今天咱们深入聊聊编译期的C语言。运行期的C语言,我在我之前的课程中已经详细讲过,感兴趣的自己去看视频
编译期,即编译器工作的期间。一般编译器,都是基于《编译原理》实现的

添加图片注释,不超过 140 字(可选)
但是C语言的编译器的实现,在《编译原理》基础上做了拓展,如图

添加图片注释,不超过 140 字(可选)
接下来详细讲讲C语言编译器的各个阶段
01 预处理
在真正编译C语言程序之前,需要对C语言程序进行预处理,预处理是由集成在编译器中的预处理器完成的
从《编译原理》的角度,词法分析是编译器的第一个阶段。但是从C语言编译器的角度,预处理是第一个阶段
预处理阶段主要完成四件事:删除注释、宏展开、文件包含、条件编译
如果你要做实验论证,gcc -E即可实现,比如:gcc -E 1.c -o 1.i
举个例子吧:删除注释、宏展开,比如代码

添加图片注释,不超过 140 字(可选)
预处理以后,注释没有了,宏展开了。C语言的预处理器,就是简单的宏替换

添加图片注释,不超过 140 字(可选)
你会发现,预处理器会在开头多生成点东西,这些东西是什么呢?有什么用呢?
这些看似神秘的 # 开头的行,实际上叫做行控制信息(line control directives),是 GCC 预处理器在输出中自动插入的特殊指令,是方便后续编译器、调试器、诊断工具理解源码位置的信息
02 词法分析
经过预处理后,得到的就是完整的C语言程序了,就可以开始编译了。词法分析器启动…
词法分析器的职责是:输入源程序,输出token

添加图片注释,不超过 140 字(可选)
来看看上面的程序生成的token

添加图片注释,不超过 140 字(可选)
如何查看C语言程序生成的token呢?clang -cc1 -dump-tokens 1.c
你可能想问:为什么用clang,而不是用gcc?因为gcc作为老牌的编译器,不支持这个功能
来看看词法分析器生成token的过程

添加图片注释,不超过 140 字(可选)
如果你想透彻理解词法分析的底层原理,你可以使用词法分析工具flex生成词法分析器,去实战。或者自己从零写一个词法分析器,体会将程序的点点滴滴转成token的过程,你的困惑就解开了…(这部分内容,我做的课程手写编程语言中有教,感兴趣的可以咨询班主任jvm-anan)
03 语法分析
拿到源程序对应的token,就可以去做语法分析了。语法分析器启动…
语法分析器的职责是:输入token,输出抽象语法树AST。后面的阶段,都是围绕AST进行的

添加图片注释,不超过 140 字(可选)
换个程序,来看看经过语法分析生成的抽象语法树

添加图片注释,不超过 140 字(可选)
对应的AST

添加图片注释,不超过 140 字(可选)
如何查看呢?两种方式:clang-check 1.c --ast-dump -- -std=c11;clang -Xclang -ast-dump -fsyntax-only 1.c
其实gcc也可以:gcc -fdump-tree-all -c 1.c,就是生成的文件太多,看起来不直观
会生成这些文件

添加图片注释,不超过 140 字(可选)
来看看token生成AST的过程,比如print语句

添加图片注释,不超过 140 字(可选)
如果你想透彻理解语法分析的底层原理,你可以使用语法分析工具bison生成语法分析器,去实战。或者自己从零写一个语法分析器,体会将token转成AST的过程,你的困惑就解开了…(这部分内容,我做的课程手写编程语言中有教,感兴趣的可以咨询班主任jvm-anan)
04 语义分析
语义分析器的职责是:输入AST,输出AST + 符号表

添加图片注释,不超过 140 字(可选)
比如程序生成的AST长这样

添加图片注释,不超过 140 字(可选)
语义分析器会遍历AST,生成符号表。这个无法查看,你可以大概理解成是这样

添加图片注释,不超过 140 字(可选)
然后是在AST表中加上注解

添加图片注释,不超过 140 字(可选)
语义分析是一种什么感觉呢?就像拿着一棵语法树边走边做笔记,遇到变量声明就记下来(符号表),遇到变量使用就去查阅笔记,发现有问题就报错
语义分析具体做哪些事情呢?我们所知的如:类型检查、类型转换、生命周期检查、控制流检查、访问权限…完整的如图
语义分析是编译器前端中逻辑最复杂、实现难度最高、语言标准依赖最强、对整体编译正确性最关键的阶段。我在我的课程手写编程语言中,做了初步的语义分析,我觉得让大家touch到那个感觉即可
06 中间代码生成
万事俱备,可以生成中间代码了。比如程序

添加图片注释,不超过 140 字(可选)
生成的中间代码长这样

添加图片注释,不超过 140 字(可选)
如何查看的呢?gcc -fdump-tree-all 1.c
前面说了,这样干会生成很多文件,后缀名是.gimple的才是
C语言的中间代码(IR)称为:GIMPLE, 三地址码形式
07 优化
在编译之前,还要做一件事:优化。我们使用gcc -O配置优化级别,就是在这个阶段完成的
gcc一共提供了5个优化
所有的优化任务
举个例子帮助大家理解编译优化,比如常量折叠,优化前

添加图片注释,不超过 140 字(可选)
优化后

添加图片注释,不超过 140 字(可选)
看到这,是不是有一种悟的感觉了…
09 代码生成
代码生成阶段是由代码生成器完成的。代码生成器的职责是:将优化后的中间表示(IR)转换为目标平台的汇编代码

添加图片注释,不超过 140 字(可选)
比如代码

添加图片注释,不超过 140 字(可选)
生成汇编代码

添加图片注释,不超过 140 字(可选)
如果你想查看C语言程序生成的汇编程序,运行时查看反汇编即可,编译时呢?这样查看:gcc -S 1.c -o 1.s
这就是C语言代码生成的all
10 编译
这个阶段就是将汇编代码编译成机器码

添加图片注释,不超过 140 字(可选)
注意,这时候还不是可执行文件,是目标文件.o

添加图片注释,不超过 140 字(可选)
如何得到目标文件呢?gcc -c test.s -o test.o

添加图片注释,不超过 140 字(可选)
理解这个阶段非常重要,因为我们写很多底层程序,比如操作系统,需要使用汇编+C语言,就是将汇编程序编译成目标文件,C语言程序编译成目标文件,然后链接成可执行文件,才得到真正的操作系统代码
11 链接
万事俱备,只欠可执行文件了。链接器启动…

添加图片注释,不超过 140 字(可选)
执行链接操作:gcc test.o -o test即可生成可执行文件

添加图片注释,不超过 140 字(可选)
如果有多个中间文件,可以将多个中间文件链接:gcc test.o 1.o 2.o -o test
注意,gcc完成链接工作,背后其实是调用链接器ld实现的
以上就是C语言程序编译期的全部,你学废了吗?
参考文献链接
人工智能芯片与自动驾驶