编译器的核心功能是把源代码翻译成目标代码:
什么是编译器
编译器的核心功能
源代码——>编译器——>静态计算——>目标代码——>计算机——>动态计算——>计算结果
这是个简单的C语言程序:
#include
int main()
{
int a = 3;
int b = 4;
int c = a + b;
printf("c=%d \n",c);
return 0;
}
通过编译器翻译成目标代码:
#include
int main()
{
00E813C0 push ebp
00E813C1 mov ebp,esp
00E813C3 sub esp,0E4h
00E813C9 push ebx
00E813CA push esi
00E813CB push edi
00E813CC lea edi,[ebp-0E4h]
00E813D2 mov ecx,39h
00E813D7 mov eax,0CCCCCCCCh
00E813DC rep stos dword ptr es:[edi]
int a = 3;
00E813DE mov dword ptr [a],3
int b = 4;
00E813E5 mov dword ptr [b],4
int c = a + b;
00E813EC mov eax,dword ptr [a]
00E813EF mov eax,dword ptr [b]
00E813F2 mov dword ptr [c],eax
printf("c=%d \n",c);
00E813F5mov esi,esp
00E813F7 mov eax,dword ptr [c]
00E813FA push eax
00E813FB push 0E85858h
00E81400 ca11 dword ptr ds:[0E89114h]
00E81406 add esp,8
00E81409 cmp esi,esp
00E8140B call _RTC_CheckEsp (0E81136h)
return 0;
00E81410 xor esi,esp
}
从左到右,一个字符一个字符地读入源程序,对构成源程序的字符
流进行扫描和分解,从而识别出一个个单词。
词法分析器的任务
字符流——>词法分析器——>单词流
# 源代码
float limitedSquare(x)
{
float x;
return (x<=-10.0)||x>10.0)?100:x*x;
}
# 词法分析器转换后
<float,->
<id,limitedSquare>
<(,->
<id, x>
<) ,->
<{,->
<float>
<id, x>
<; ,->
<return,->
<(,->
<id, x>
<op,"<=">
<num, -10.0>
<op, "||">
<id, x>
<op, ">=">
<num, 10.0>
<) ,->
<op, "?">
<num, 100>
<op, ":">
<id, x>
<op, "*">
<id, x>
<; ,->
<},->
根据语言的语法规则,把单词符号串组成各类语法单位。
语法分析:是否符合语法
语法分析器是编译器的核心模块:
语法分析器的任务
单词流——>语法分析器(按照某种语言规则)——>语法树
语义检查:
语义分析器的功能
收集标识符的属性、类型(Type)、种属(Kind)、存储位置、长度、值、作用域、参数和返回值等相关信息。
中间代码是一种抽象的表示形式,通常比源代码更接近底层的目标机器代码,但又比目标机器代码更容易进行分析、优化和跨平台移植。中间代码可采用多种形式,如虚拟机指令、三地址码、静态单赋值(SSA)等表示。
在尽量不改变程序语义的前提下,对中间代码进行分析和优化,以产生执行速度较快的机器代码,提高程序在执行效率、内存占用和功耗等方面的性能。
生成可重定位的机器代码或汇编代码,以便在目标平台上执行程序。
基本功能是记录源程序中使用的标识符,并收集与每个标识符相关的各种属性信息,记录到符号表中。
例:int a,b;
名字 | 记号 | 类型 | 种属 | …… | addr |
---|---|---|---|---|---|
a | id1(25) | int | 简变 | 0 | |
b | id2(25) | int | 简变 | 4 |
以上各阶段难免会遇到错误:
编译器由多个阶段组成,每个阶段都要处理不同的问题
使用不同的理论,数据结构,和算法
因此,编译器设计中的重要问题是如何合理的划分组织各个阶段
接口清晰
编译器容易实现与维护