第9章 符号表

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第9章 符号表管理技术
在编译的各个阶段经常要收集、使用出现在源程序中的各种信 息,通常把这些信息用一些表格进行记录、存储和管理,如常量表、 数组信息表等等,这些表统称为符号表。
符号表的作用∶ 保存各类标识符的属性 检查语义的正确性 作为目标代码生成阶段地址分配的依据 符号表的作用是通过插入和检索符号表中记录的标识符属性来 实现的。这些属性(如名字、类型、维数等)在声明语句中可直接找 到,有些可根据程序中标识符出现的上下文间接地获得。 在编译时,源程序中每出现一次标识符,就要与符号表打一次 交道,主要工作是查表和存取操作,因此,与符号表的交互占据了 大量的编译时间。所以,如何有效地操作符号表直接影响编译的效 率。
非块程序结构语言的符号表结构 所谓非块程序结构语言,是指用它编写的每一个可独立编译 的程序是一个不包含子块的单一模块程序,该模块中声明的所有 变量是属于整个模块的。非块程序结构语言的符号表有几种组织 方式,其中比较简单的是无序表和有序表。 1、无序表 无序符号表也称为线性表。构造一个符号表最简单的方法 是变量的属性项按变量被声明的先后顺序填入表中。无序表插 入和查找操作比较简单,但查找效率低。但如果符号表较小, 采用无序表则非常合适。无序表的优点是结构简单、节省空间, 添加及查找操作简单、易于实现。
何时建立和访问符号表 有的编译程序,只在语义分析时才创建符号表。在语义分 析阶段,遇到声明语句时会根据标识符的类别、名字属性创建 符号表记录。同时对源程序代码需要检查语义的正确性。 在符号表中记录的标识符的属性信息会在代码生成阶段用 于产生目标代码的指令序列。 因此,直到语义分析和代码生成阶段,许多与变量有关的 属性才能够相继填入符号表。 总之,如果在词法分析阶段创建符号表,只能在符号表中 将标识符的名字填入符号表,而其他属性则要在语义分析和代 码生成阶段填入。如果在语义分析阶段创建符号表,那么与符 号表打交道的就仅局限于语义分析和代码生成部分。
栈式符号表
块结构语言程序中允许存在重名变量的声明,但重名变量 不能出现在同一块中,因此所有标识符插入前要检查当前处理 的块中是否有同名变量。其方法是:从栈顶单元(TOP-1)开始 到块索引栈的栈顶单元所指的单元,逐一检查,如果有与要插 入的变量同名,则表示源程序中存在变量重复声明的错误;如 果没有,才可将该标识符的属性入栈。 查表操作要对符号栈进行从顶(TOP-1)到底进行线性搜索, 这样确保找到的变量满足局部变量优先于全局变量的规则。由 于栈式符号表中记录是无序的,因而查询效率比较差。
块程序结构语言的符号表组织
块程序结构语言的概念
所谓块程序结构语言是指程序模块可包含嵌套的子模块, 每一子模块可以有一组自己的局部变量。
按块程序结构语言的规定:变量的作用域是定义它的块程 序;同一块内的变量不能重名,但不同块以及嵌套块之间的变 量可以重名,因而某变量的声明可与嵌套块的内层变量同名, 使用时局部变量优先。
非块程序结构语言的符号表结构 2、有序表 在编译过程中,由于查找符号表的次数远大于插入符号表的 次数,所以如何提高符号表的查找效率直接影响编译的效率。 有序符号表的表项是根据变量名按字母顺序存放的。因此, 每次插入符号表前,首先要进行查表工作,以确定要插入的符号 在符号表中的位置,然后将符号插入。这样难免会造成原有一些 符号的移动,所以,这种符号表结构在插入符号时开销较大。对 于有序表,最常用的查找技术是折半查找法。
ComputerX1Fຫໍສະໝຸດ BaiduRM1
符号表管理技术
目标地址:标识符主要作为变量名字。程序中每个变量都必须 有一个相应的目标地址,该地址是为该变量分配的内存地址(可能 是相对的)。
当声明一个变量时,就要为该变量分配内存地址,并将其分配 的地址填入符号表中。当该变量在程序的其它处被引用时,可以从 符号表中查询该地址,并填入存取该变量值的目标代码中。 对于采用静态存储分配的语言(如FORTRAN),分配的地址是按 连续的顺序分配的。
下面为一段C程序,右边给出当编译程序编译到此处时的有效变量。 1. real x,y; 2. char name; name,y,x 3. int fun1(int ind) ind,fun1,name, y,x 4. { int x; x,ind, fun1,name, y,x 5. x=m2(ind+1); 6. } fun1,name, y,x 7. int fun2(int j) j,fun2, fun1,name, y,x 8. { 9. { int f[10]; 10. bool test1; test1,f, j,fun2, fun1,name, y,x 11. } j,fun2, fun1,name, y,x 12. } fun2, fun1,name, y,x 13. main() main, fun2, fun1,name, y,x 14. { 15. char name; name, main, fun2, fun1,name, y,x 16. x=2;y=5; 17. printf("%d\n",fun1(x/y)); 18. }
栈式符号表
对于块程序结构语言,其最简单的符号表结构为栈式符号 表,它包括一个符号表栈及一个块索引栈。
符号表栈记录变量的属性,块索引栈指出每个块的符号 表的开始位置。栈式符号表操作过程十分简单,当遇到变量声 明时,将包含变量的属性压入堆栈;当遇到块程序开始时,将 当前的符号表栈顶位置压入块索引栈,从而开始一个新块的变 量处理;当到达块程序结尾时,则根据块索引栈指出的本块的 开始为位置,将该块程序中声明的所有变量记录弹出堆栈,从 而使局部声明的变量在块外不再存在。
栈式符号表
栈式符号表的插入操作:
首先,刚开始编译时,设符号表栈顶指针TOP为0,当第一个 标识符出现时,将该标识符的属性入栈,同时将该标识符的地址 0压入块索引栈,然后栈顶指针TOP为1。 后面再遇到标识符时,如果新的标识符与栈顶的标识符在同 一块中,则只需将新记录压入符号表栈顶单元,然后,栈顶指针 TOP加1(图a)。 如果新的标识符与栈顶的标识符不在同一块中,表示刚才处 理的程序块嵌套着一个程序块,而现在进入了这个嵌套的程序块 中,则要进行定位操作,即将栈顶指针TOP入块索引栈,再将该 标识符属性压入符号栈,然后栈顶指针TOP加1(图b、c)。 当编译遇到程序块的结尾时要进行重定位操作,即将块索引 栈的栈顶单元出栈并将内容赋给栈顶指针TOP。 注意:栈顶指针TOP始终指向符号表栈顶第一个空闲的存储单元。
符号表的组织和内容
符号表的管理程序应该具有快速查找、快速删除、易于使 用、易于维护的特点。符号表具体包含哪些内容,属性的种类 和多少在一定程度上取决于程序设计语言的性质。 符号表基本上都是由一些表项组成的二维表格,每个表项 可分为两部分: 第一部分是名字域,用来存放符号的名字; 第二部分是属性域,用来记录与该名字相对应的各种属性 和特征。
从编译系统建造符号表的过程来划分,符号表可分为静态表 和动态表两大类:一是静态表,即在编译前就已经构造好了的符 号表,如保留字表、标准函数名表等。二是动态表,即在编译过 程中根据需要构造的符号表,如变量表、数组信息表、过程信息 表等等。
符号表上的操作
在符号表上最常执行的操作是插入和查找。 对于变量都必须显式说明的语言:插入操作发生在变量声明 时,即处理一个变量声明语句的时候。在进行插入操作前,首 先要查符号表,以确定符号表中无该变量记录,即变量不是重 复声明。 查找符号表操作发生的次数很多。除了插入前要先查符号表 外,每次变量引用时都要查找符号表。如果查到,那么查找出 的信息将用于语义检查和代码生成,同时可根据变量的上下文 对未声明(或预先声明的)变量的有关属性进行推测,发出错误 或警告信息、或相应的改正信息;如果没有查到,说明存在变 量没有声明就引用的错误,应报告相应的错误信息。
第9章 符号表管理技术 符号表的作用∶ 检查语义的正确性 上下文敏感成分的分析实质上是语法分析的内容。但我们的语 法分析是以上下文无关文法为基础的,没有考虑上下文敏感成分的 处理,所以必须在语义分析时加以考虑。 属于上下文敏感的语法规定包括: 标识符通常先定义后使用,但在同一个块中,标识符不应重复 定义; 实参表中的实参个数与类型必须与相应形参表中的形参个数与 类型一致; 表达式中运算对象的类型必须与运算符相容,等等。
当编译完第2行时,符号表中应含有变量name,y,x的记录。
编译到第4行时,符号表中应含有变量x,ind, fun1,name, y,x的 记录,此时,符号表同时含有两个变量x的记录,而在函数fun1 内有效的变量是局部变量x,即第4行声明的变量。 而编译到第6行时,符号表中有变量fun1,name, y,x的记录, x,ind失效。
① 定长存贮方法,即为标识符名字域规定一个宽度,标识符按左对齐 方式存放在其中,特点是简单且存取速度快,缺点是空间利用率低,标识符 长度不能超过名字域的宽度。
② 集中存贮方法,即开辟一个存放所有标识符的缓冲区,而在标识符 名字域中只存放标识符在缓冲区中的偏移地址和标识符的长度。特点是存贮 效率高,标识符无长度限制,但存取效率低。 名字位置 1 9 17 … 名字长度 8 2 5 … 图1 集中存贮方法符号表 其它属性
编译到第10行时,有效变量为test1,f, j,fun2, fun1,name, y,x。
而编译到第11行时,符号表中有变量j,fun2, fun1,name, y,x的 记录,test1,f失效。 编译到第15行时,符号表中应含有变量name, main, fun2, fun1,name, y,x的记录,此时,在主函数main内使用的变量x和y 都是全局变量。
符号表的组织和内容 几种主要属性通常是需要的。 1.名字
2.符号类型
3.符号存储类别 4.符号的作用域及可视性 5.符号变量的存储分配信息(目标地址) 6.符号的其它属性
符号表管理技术
名字:名字必须常驻在表中,因为它是在语义分析和代码生成中识别一个具
体标识符的依据。在符号表的组织中,一个要解决的重要问题是标识符长度 的可变问题。对标识符名字的处理要考虑语言中对标识符长度的规定是定长 还是不定长。根据标识符的定义特点,通常采用的存储方法有两种:
第9章 符号表管理技术
符号表的作用∶ 作为目标代码生成阶段地址分配的依据。 对于变量标识符,在目标代码生成时需要确定其存储分配的位 置。主要依据符号变量的存储类别及被定义的位置来确定。
何时建立和访问符号表 符号表可在词法分析时创建,也可在语义分析时创建。
有的编译程序中,符号表在词法分析阶段创建,符号表此 时只含有标识符的名字,其它属性要在语义分析阶段填入。 当从源程序中识别出一个标识符时,就以此名字查符号表, 若表中尚无此登记项,则将该名字列入表中。 语法分析阶段只检查源程序语法的正确性,一般不使用符号 表。
原则上,一个编译程序使用一张符号表就够了,但在源程序 中,由于不同种类的符号起着不同的作用,相应于各类符号所需 记录的属性往往不同,因此,多数编译程序都是根据符号的不同 种类,分别建立不同的符号表,如常数表、变量名表、数组信息 表、过程信息表、保留字表、特殊符号表、标准函数名表等等, 这样处理起来更方便一些。
对于采用动态存储分配的语言,每个程序块内的变量连续分配, 这是一个相对地址,运行时还要根据该程序块分配的数据区的起始 地址和变量的相对地址计算出变量的绝对内存地址。 如果标识符表示的是函数名或子程序名,则目标地址是该函数 或子程序代码的开始地址。如果是数组名,则应为数组模板的起始 地址。
符号表管理技术 类型:不同数据类型的变量占据不同大小的内存空间,另外类 型检查是语义分析的一项重要工作,所以符号表中要保存每个 标识符的数据类型,以便分配内存和进行类型检查。 维数及参数个数:这两个属性对类型检查都是重要的。在数组 引用时,其维数应当与数组声明中所定义的维数一致,类型检 查阶段必须对这种一致性进行检查,另外,维数也用于数组元 素地址的计算。 过程调用时,实参个数也必须与形参个数一致。实际上, 在符号表组织中,把参数的个数看成它的维数是很方便的,因 此可以将两个属性合并成一个,另外这种方法也是协调的,因 为对这两种属性所做的类型检查是类似的。
相关文档
最新文档