编译原理 第八章符号表
- 格式:doc
- 大小:70.00 KB
- 文档页数:4
编译原理符号表的应用1. 什么是编译原理符号表编译原理中的符号表是一种数据结构,用于记录程序中各个符号的相关信息,包括变量名、函数名、常量等。
在编译过程中,符号表起着重要的作用,可以进行词法分析、语法分析和语义分析等过程中的变量和函数的命名检查、重名检查以及类型检查等功能。
2. 符号表的组织结构符号表可以采用不同的组织结构,最常见的有线性表、散列表和树等。
下面列举了几种常见的符号表组织结构:•线性表:符号表可以通过数组或链表等数据结构来表示。
•散列表:采用散列函数对符号进行映射,能够快速地查找符号。
•树:符号表可以用二叉搜索树、AVL树或红黑树等数据结构来表示,支持快速的查找、插入和删除操作。
3. 符号表在编译过程中的应用符号表在编译过程中扮演着重要的角色,下面介绍了符号表在不同阶段的应用:3.1 词法分析阶段在词法分析阶段,编译器通过符号表来记录程序中出现的各个标识符的信息,包括变量名、函数名和常量等。
符号表可以用来进行标识符的重名检查,以及维护标识符的属性信息,比如变量的类型、作用域和内存地址等。
3.2 语法分析阶段在语法分析阶段,编译器需要判断语法是否正确,并生成语法树。
符号表在此阶段可以用来进行各种类型的语法检查,比如检查函数参数的类型、检查类型转换的合法性等。
符号表还可以用来维护函数的参数表和局部变量表等信息。
3.3 语义分析阶段在语义分析阶段,编译器需要对代码进行语义检查,包括类型检查、作用域检查等。
符号表是进行这些检查的重要依据,通过符号表可以判断变量是否被定义、变量的作用域和类型是否匹配等。
3.4 中间代码生成阶段在中间代码生成阶段,编译器需要将源代码转换成中间代码,符号表可以用来生成中间表示时的参考依据。
符号表可以用来维护中间变量的属性信息,并生成中间代码时进行类型转换的判断。
3.5 代码优化和目标代码生成阶段在代码优化和目标代码生成阶段,符号表可以用来进行变量的寄存器分配和内存分配等操作。
第八章符号表编译过程中编译程序需要不断汇集和反复查证出现在源程序中各种名字的属性和特征等有关信息。
这些信息通常记录在一张或几张符号表中。
符号表的每一项包含两部分,一部分是名字(标识符),另一部分是此名字的有关信息。
每个名字的有关信息一般指种属(如简单变量、数组、过程等)、类型(如整、实、布尔等)等等。
这些信息将使用于语义检查、产生中间代码以及最终生成目标代码等不同阶段。
编译过程中,每当扫描器识别出一个单词后,编译程序就查阅符号表,看它是否已在其中。
如果它是一个新名就将它填进表里。
它的有关信息将在词法分析和语法-语义分析过程中陆续填入。
符号表中所登记的信息在编译的不同阶段都要用到。
在语义分析中,符号表所登记的内容将用于语义检查(如检查一个名字的使用和原先的说明是否相一致)和产生中间代码。
在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。
对于一个多遍扫描的编译程序,不同遍所用的符号表也往往各有不同。
因为每遍所关心的信息各有差异。
本章重点:符号表的一般组织和使用方法。
第一节符号表的组织和使用信息栏通常包含许多子栏和标志位,用来记录相应名字的种种不同属性。
由于查填符号表一般都是通过匹配名字来实现的,因此,名字栏也称主栏。
主栏的内容称为关键字(key word)。
虽然原则上说,使用一张统一的符号表也就够了,但是,许多编译程序按名字的不同种属分别使用许多符号表,如常数表、变量名表、过程名表等等。
这是因为,不同种属名字的相应信息往往不同,并且信息栏的长度也各有差异的缘故。
因而,按不同种属建立不同的符号表在处理上常常是比较方便的。
对于编译程序的符号表来说,它所涉及的基本操作大致可归纳为五类:1、对给定名字,确定此名是否在有中;2、填入新名;3、对给定名字,访问它的有关信息;4、对给字名字,填写或更新它的某些信息;5、删除一个或一组无用的项。
不同种类的表格所涉及的操作往往也是不同的。
上述五方面只是一些基本的共同操作。
编译原理符号表的作用介绍编译原理中的符号表是一个重要的数据结构,用于存储程序中的标识符及其相关信息。
标识符可以是变量、常量、函数名等,在编译过程中需要进行词法和语义分析,符号表提供了一个地方来管理这些标识符,并为编译器的其他模块提供必要的信息。
作用符号表在编译过程中起着关键作用,它具有以下几个主要作用。
1. 标识符的声明符号表记录了程序中所有标识符的声明情况,包括标识符的类型、作用域等信息。
对于变量,符号表可以记录其数据类型和内存地址;对于函数,符号表可以记录其参数列表、返回值类型等。
编译器可以通过符号表查找标识符的声明信息,并根据需要进行语义检查和代码优化。
2. 标识符的引用和解析编译过程中,标识符可能会被多次引用,符号表用于解析标识符的引用。
编译器可以根据符号表中的信息确定标识符的类型、作用域等,从而进行语义检查和类型推导。
如果编译器在符号表中找不到对应的标识符,就会报错或警告,提示可能存在的错误。
3. 作用域管理符号表还可以用于管理标识符的作用域。
在程序中,不同的代码块可能定义了相同名称的标识符,符号表可以通过作用域信息来区分这些标识符。
当编译器遇到一个标识符时,它可以在符号表中查找该标识符的作用域,并根据作用域规则来解析标识符的含义。
4. 错误检测和提示符号表还可以用于错误检测和提示。
编译器可以通过符号表判断标识符是否已经定义或声明,以及是否满足相应的语义规则。
如果标识符在符号表中已经存在多个定义,编译器可以发现这种错误,并给出相应的错误提示信息。
符号表的组织结构为了高效地实现符号表的作用,通常采用哈希表或树形结构来组织符号表。
下面是一些常见的符号表组织结构。
1. 线性表符号表可以使用线性表结构进行组织,例如数组、链表等。
线性表结构简单直观,适用于较小规模的符号表。
但对于大规模的符号表,线性表的查找效率较低。
2. 哈希表哈希表是一种基于键值对存储的数据结构,可以快速地查找和插入数据。
符号表中的标识符可以作为哈希表的键,对应的信息可以作为值进行存储。
编译原理符号表符号表是编译器中一个非常重要的数据结构,用于存储程序中的标识符(如变量、函数名等)和对应的属性信息(如数据类型、作用域等)。
在编译器的各个阶段,都需要使用符号表来进行词法分析、语法分析、语义分析等操作,因此符号表设计的好坏直接影响到编译器的质量和效率。
一般来讲,符号表可以被看作是一个以标识符为键、以属性信息为值的映射表。
在编译器的词法分析阶段,源代码中的每个标识符都会被扫描并加入符号表中,同时为每个标识符生成一个唯一的“id”(也称为“符号表条目”)作为在后续处理中访问符号表的索引。
在编译器的语法分析和语义分析阶段,编译器会利用符号表进行语法分析和语义检查。
例如,在语法分析阶段,编译器需要判断变量是否被正确声明和使用,因此需要在符号表中查找变量的属性信息;而在语义分析阶段,编译器需要对表达式进行类型检查或者函数调用进行参数匹配,因此也需要在符号表中查找相关的属性信息。
需要注意的是,符号表的实现需要考虑到标识符的作用域、重复定义、名称空间等问题。
一般来说,编译器需要支持不同作用域之间的变量共存和访问,因此需要为不同的作用域维护不同的符号表。
当在一个新作用域中遇到相同的标识符时,编译器应该创建新的符号表条目;而在同一作用域中出现重复定义时,编译器应该抛出错误信息。
同样需要注意的是,符号表的实现也需要考虑到数据结构的效率和空间占用。
一些常用的实现方式包括基于哈希表的实现、基于树的实现(如平衡树、二叉查找树等)等。
在编译器优化阶段,符号表的实现也会影响编译器生成的目标代码的质量和效率。
例如,在常量表达式优化中,编译器使用符号表来维护常量的值和类型信息,从而可以直接进行常量表达式的求值,而不必在运行时才计算。
总的来说,在编译器中,符号表是一个极其重要的数据结构,对于编译器的性能和代码质量有着重要的影响。
因此,在设计和实现编译器时,需要认真考虑符号表的性能和可扩展性,并且根据具体的编程语言特性进行相应的优化。
第八章符号表
知识结构:
符号表的组织与作用
符号表符号表的组织
符号表的管理
第一节符号表的组织与作用
一、符号表的作用
1、符号表是编译程序保存(记录)源程序中各种名字的属性和特征信息的各种表格(数据结构);
2、符号表中信息在编译各阶段都要使用,编译结束生成目标代码后,符号表中信息也随之删除。
3、编译结束时,符号表中的信息体现在目标代码的存储单元的地址。
二、符号表的组织方式
符号表最简单的组织方式是各项各栏所占用的存储单元的长度都是固定的。
易于组织、填写和查找。
例:符号表
中间代码:
(+, A, B,T1)
(+ , 15, 16 ,T1)
目标代码:
LD R0 A—地址(a+4)
ADD R0 B—地址地址中体现了存储单元的大小。
第二节整理与查找
一、线性表
构造符号表的最简单办法是按关键字出现的顺序填写各个项。
可以用一个一维数组来存放名字及有关信息。
二、符号表的操作
⑴查找
⑵填入
⑶访问信息
⑷填入或更新信息
⑸删除
三、符号表的查找
编译工作的大部分时间花费在查、填符号表,对编译的效率有直接的影响。
⑴对折查找
⑵二叉树
⑶杂凑技术
第三节符号表的内容
一、符号表的类型
⑴变量
类型,种属,长度,相对数,数组内情向量
⑵过程
过程名,外过程,形参
⑶常数表
⑷标号表
二、符号表各项的设计
表项内容:
⑴名字和信息定长
符号表
⑵名字和信息不定长。
确定符号表的组织方式,一般应包括名字栏和信息栏,其中名字栏作为关键字。
要考虑能够存储有关名字的信息,并可以高效地完成如下操作:1.查找:根据给定的名字,在符号表中查找其信息。
如果该名字在符号表中不测试范例:procedure test; var b,c,i:integer;begin b:=1; if a>b thenc:=a+b elsec:=a-b;for i:=0 to 3dobeginc:=i;end;end;符号表的结构#include<stdio.h>#include<string.h>#include<ctype.h>#include<stdlib.h>struct{ int m;char name[20]; char inf[20];}co[999]; int num;include 称为文件包含命令,其意义是把尖括号""或引号<>内指定的文件包含到本程序中,成为本程序的一部分。
被包含的文件通常是由系统提供的,其扩展名为.h 而stdio为standard input output的缩写,意为“标准输入输出” .。
#include<string,h>这是C语言/C++中的字符串处理函数的头文件。
#include <ctype.h>是用作字符处理的,#include <stdlib.h>是用于定义杂项函数及内存分配函数,最后是一个结构函数,分别定义了符号名,信息,个数等变量。
符号显示在C-Free中用了void display(),将已经输入进系统的符号全部都显示出来 3. 符号查找本符号表系统中用void find() ,并通过for循环将所要查找的符号及其信息全都显示出来。
如果查找的字符存在,则在显示之前,运用system(“cls”)进行了清屏,把之前屏幕上所显示的内容全部清除,同时,在显示完成以后,又用了if 语句判断是否要删除此符号及其信息。
编译原理符号表的作用一、引言编译器是将高级语言翻译成机器语言的程序,其主要功能是将源代码转换为可执行的目标代码。
在编译过程中,符号表是一个非常重要的数据结构,用于存储程序中出现的各种符号信息。
本文将介绍符号表的作用及其实现原理。
二、符号表的定义符号表是编译器中用于存储程序中出现的各种符号信息的数据结构,包括变量名、函数名、类型名等标识符及其属性信息。
它通常由一个哈希表和多个链表组成。
三、符号表的作用1. 语法分析阶段:在语法分析阶段,编译器会扫描源代码并生成相应的语法树。
同时,在扫描过程中,编译器会将每个标识符添加到符号表中,并记录其类型、作用域等属性信息。
这些信息可以在后续阶段中被使用。
2. 语义分析阶段:在语义分析阶段,编译器会对程序进行类型检查、作用域检查等操作。
这些操作需要使用到之前保存在符号表中的属性信息。
3. 代码生成阶段:在代码生成阶段,编译器会将源代码转换为目标代码。
在此过程中,编译器需要根据符号表中的信息生成目标代码中的符号表。
四、符号表的实现1. 哈希表:符号表通常由一个哈希表和多个链表组成。
哈希表用于快速查找标识符,并将其插入到相应的链表中。
2. 链表:每个链表对应一个作用域,用于存储该作用域中出现的所有标识符及其属性信息。
当进入一个新的作用域时,编译器会创建一个新的链表,并将其添加到符号表中。
当离开该作用域时,编译器会删除该链表。
3. 属性信息:在符号表中,每个标识符都有相应的属性信息,包括类型、作用域、地址等。
这些信息可以在后续阶段中被使用。
五、总结在编译过程中,符号表是一个非常重要的数据结构。
它可以存储程序中出现的各种标识符及其属性信息,并在后续阶段中被使用。
符号表通常由一个哈希表和多个链表组成,其中哈希表用于快速查找标识符,链表用于存储每个作用域中出现的所有标识符及其属性信息。
通过对符号表的实现和使用,编译器可以更加准确地将源代码转换为目标代码。
第八章符号表编译过程中编译程序需要不断汇集和反复查证出现在源程序中各种名字的属性和特征等有关信息。
这些信息通常记录在一张或几张符号表中。
符号表的每一项包含两部分,一部分是名字(标识符),另一部分是此名字的有关信息。
每个名字的有关信息一般指种属(如简单变量、数组、过程等)、类型(如整、实、布尔等)等等。
这些信息将使用于语义检查、产生中间代码以及最终生成目标代码等不同阶段。
编译过程中,每当扫描器识别出一个单词后,编译程序就查阅符号表,看它是否已在其中。
如果它是一个新名就将它填进表里。
它的有关信息将在词法分析和语法-语义分析过程中陆续填入。
符号表中所登记的信息在编译的不同阶段都要用到。
在语义分析中,符号表所登记的内容将用于语义检查(如检查一个名字的使用和原先的说明是否相一致)和产生中间代码。
在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。
对于一个多遍扫描的编译程序,不同遍所用的符号表也往往各有不同。
因为每遍所关心的信息各有差异。
本章重点:符号表的一般组织和使用方法。
第一节符号表的组织和使用信息栏通常包含许多子栏和标志位,用来记录相应名字的种种不同属性。
由于查填符号表一般都是通过匹配名字来实现的,因此,名字栏也称主栏。
主栏的内容称为关键字(key word)。
虽然原则上说,使用一张统一的符号表也就够了,但是,许多编译程序按名字的不同种属分别使用许多符号表,如常数表、变量名表、过程名表等等。
这是因为,不同种属名字的相应信息往往不同,并且信息栏的长度也各有差异的缘故。
因而,按不同种属建立不同的符号表在处理上常常是比较方便的。
对于编译程序的符号表来说,它所涉及的基本操作大致可归纳为五类:1、对给定名字,确定此名是否在有中;2、填入新名;3、对给定名字,访问它的有关信息;4、对给字名字,填写或更新它的某些信息;5、删除一个或一组无用的项。
不同种类的表格所涉及的操作往往也是不同的。
上述五方面只是一些基本的共同操作。
符号表最简单的组织方式是让各项各栏所占的存储单元的长度都是固定的。
这种项栏长度固定的表格易于组织、填写和查找。
对于这种表格,每一栏的内容可直接填写在有关的区段里。
例如,有些语言规定标识符的长度不得超过8个字符,于是,我们就可以用两个机器字作为主栏(假定每个机器字可容四个字符)每个名字直接填写在主栏中。
若标识长度不到8个字符,则用空白符补足。
这种直接填写式的表格形式如下:但是,有许多语言对标识符的长度几乎不加限制,或者说,标识符的长度范围甚宽。
譬如说,最长可容许由100个字符组成的名字。
在这种情况下,如果每项都用25个字作主栏,则势必会大量浪费存储空间。
因此,最好用一个独立的字符串数组,把所有标识符都存放在其中。
在符号表的主栏放一个指示器和一个整数。
指示器指出标识符在字符串数组中的位置;整数代表此标识符的长度。
这样,符号表的结构就如下图所示:这是一种用间接方式安排名字栏的办法。
类似地,如果各种名字所需的信息(INF-ORMA TION )空间长短不一,那么,我们可把一些共同属性直接登记在符号表的信息栏中,而把某些特殊属性登记在别的地方,并在信息栏中附设一指示器。
指向存放特殊属性的地方。
编译开始时,符号表或者是空的,或者预先存放了一些保留字和标准函数名的有关项。
在整个编译过程中,符号表的查填频率是非常高的。
编译工作的相当一大部分时间是花费在查填符号表上。
所以,研究表格结构和查填方法是一件非常重要的事情。
下面,我们简略地介绍几种一般的表格构造法和查填法。
第二节 符号表项的排列与查找符号表作为一个多元组,表中元组之间的排列组织是构造符号表的重要成分。
在编译程序的整个工作过程中,符号表被频繁也用来建立表项,找查表项,填充和引用表项的属性。
因此表项的排列组织对该系统运行的效率起着十分重要的作用。
在“数据结构”技巧的讨论中提供了很多有关多元组表格的组织方法和它们有关的操作算法。
而在编译程序中,符号表项的组织传统上采用三种构造方法。
即线性法,二分法及散列法。
1、线性组织 这种方法规定符号表项中按它的符号被扫描到的先后顺序建立。
例如有一程序中出现符号的情况如下:…………………a …………………b ………a …………………d ………c …………………b …… ……则符号表中表项排列将如图8-2-1其中h 表示该符号表之表头,是表的开始位置,p 表示该符号表的表项是符号表当前的结束位置,在编译程序工作过程中,扫描得到之新符号总是登入到p 的位置,而p 又取下一新位置,编译程序开始工作时p 处于h 表头位置。
这种组织方式在“数据结构”的讨论中可知它的管理简单但运行效率低,特别当表项数目较大后效率就非常低。
因为它没有空白项,因此存储空间效率高,但对于符号个数不确定的情况下,无法事先确定该符号表的总长度。
对于事先能确定符号个数且符号个数不大(公认为小20)时采用线性表组织是非常合适的。
2、排序组织及二分法 语言中任何符号都是由一个或几个字符拼写而成的,在机器中是用字符代码(通常是ASCII 或EBCDIC 代码)表达。
因每一个符号在机器内都是由这种字符代码串来表示。
排序组织的符号表,就是在符号表中的表项按其符号的字符代码串(可以看成一个整数值)的值的大小从大到小(或从小到大)排列的。
对上述例2中的符号出现情况按排序组织得到的符号表将如图8-2-2。
编译扫描的次序是a,b,d,c 。
由于c 代码小于d 代码,因此c 应在d 表项之前。
关于排序表的表项建立及符号查找,通常采用“二分法”。
详细的组织和算法在有关“数据结构”的书中可找到。
排序表的空间组织和存储开销与线性表基本相同,但排序表的运行效率要比线性表高,算法复杂性也高于线性表。
排序表有很多变形结构方式,如二叉树结构等,也在编译程序中可根据空间开销和运行效率等要求作适当的选取,这儿不去详细讨论,因为它不属编译范畴。
3、散列组织 对于表格处理来说,根本问题在于如何保证查表与填表两方面的工作都能高效地进行。
对于线性表来说,填表快,查表慢。
而对于二分法而言,则填表慢,查表快。
杂凑法是一种争取查表、填表两方面都能高速进行的统一技术。
这种办法是:假定有一个足够大的区域,这个区域是以填写一张含N 项的符号表。
我们希望构造一个地址函数H ,对任何名字SYM ,H (SYM )取值于0至N —1之间。
这就是说,不论对SYM 查表或填表,我们都希望能从H (SYM )获得它的表中的位置。
例如,我们用无符号整数作为项名,令N=17,把H (SYM )定义为SYM ÷N 的余数。
那么,名字ˋ09ˊ将被置于表中的第9项,ˋ34ˊ 将被置于表中的第0项,ˋ171ˊ将被置于表中的第1项,如此等等。
对于地址函数H 有两点要求:第一,函数的计算要简单、高效;第二,函数值能比较均匀地分布在0至N —1之间。
例如,若取N 为质数,把H (SYM )定义为SYM ÷N 的余数就是一个相当理想的函数。
构造函数H 的办法很多,通常是将符号名的编码杂凑成0~N~1间的某一个值。
因此,地址函数H 也常常称为杂凑函数。
注意,杂凑函数的选择往往和具体计算机系统的字符编码有关。
如果是对数目确定的已知符号名,我们可以通过试验,精挑细选,构造出一个一一对应函数。
如果杂凑函数是用来产生用户的标识符表的,由于用户使用标识是随机的,而且标识符的个数也是无限的(虽然在一个源程序中所有的标识符的全体是有限的),因此,企图构造一一对应的函数当然是徒劳的。
在这种情况下,除了希望函数值的分布比较均匀之外,我们还应没法解决“地址冲突”的问题。
以N=17,H (SYM )为SYM÷N 的余数为例,由于H (ˋ05ˊ)= H (ˋ22ˊ)=5,若表格的第5项已为ˋ05ˊ所占,那么,后来的ˋ22ˊ应放在哪里呢?解决地址冲突的办法很多。
我们这里只介绍一种最简单的线性查填解决法。
这种办法的填表过程是:对任何名字SYM ,令H (SYM )=h ,若第h 项为空,则直接把SYM 填入。
若h 项不空,则看第h+1(mod N )项,为空则填入,否则继续考察第h+2(mod N )项,如此反复,直至或者把SYM 填为第h+i (mod N )项;或者到达h+N=h (mod N )(说明表区已满),无法再填入。
查表过程与此相仿:令H (SYM )=h ,若第h 项为空,则说明SYM 不在表中,若SYM 等于第h 项的名字,则宣布查找成功,且SYM 的项数为h 。
否则,继续考察第h+1(mod N )项。
如此反复,直至或者在第h+i (mod N )项中找到SYM ;或者h+i (mod N )项为空,说明SYM 不在表中;或者到达h+N= h (mod N ),同样说明SYM 不在表中。
详细的组织和算法在有关“数据结构”的书中可找到。
第三节 习题一、单项选择题1、编译程序使用 区别标识符的作用域。
a. 说明标识符的过程或函数名b. 说明标识符的过程或函数的静态层次c. 说明标识符的过程或函数的动态层次d. 标识符的行号2、在目标代码生成阶段,符号表用于 。
a. 目标代码生成b. 语义检查c. 语法检查d. 地址分配3、过程信息表不包含 。
a. 过程入口地址b. 过程的静态层次c. 过程名d. 过程参数信息4、下列关于标识符和名字叙述中,正确的是。
a. 标识符有一定的含义b. 名字是一个没有意义的字符序列c. 名字有确切的属性d. a~c都不正确解答:1、b 2、d 3、b 4、c二、多项选择题1、符号表的每一项均包含。
a. 名字栏b. 类型栏c. 信息栏d. 值栏e. a~d均包含2、对编译程序所用到的符号表,涉及的操作有。
a. 填写或更新信息栏内容b. 填入新名c.给定名字,访问它的有关信息d. 杂凑技术e.线性表和排序二叉树3、源程序中的错误一般有。
a. 词法错误b. 语法错误c. 语义错误d. 编译错误e. 违反环境限制的错误解答:1、a 、c 2、a、b、c 3、a、b、c、e三、填空题1、符号表中名字栏内容有两种填写方式,它们是填写和填写。
2、词法分析阶段的错误主要是,可通过的办法纠正错误。
3、符号表中名字的有关信息在和过程中陆续填入。
4、在目标代码生成阶段,符号表是的依据。
解答:1、标识符标识符地址及长度2、拼写错误最小距离匹配3、词法分析语法语义分析4、地址分配四、问答题:1、在编译过程中为什么要建立符号表?解答:在编译过程中始终要涉及到对一些语法符号的处理,这就需要用到语法符号的相关属性。
为了在需要时能找到这些语法成分及其相关属性,就必须使用一些表格来保存这些语法成分及其属性,这些表格就是符号表。