导航:首页 > 源码编译 > 符号表在编译器中的作用

符号表在编译器中的作用

发布时间:2023-01-24 03:26:25

1. 编译器能够完成的工作是

1. 词法分析词法分析器根据词法规则识别出源程序中的各个记号(token),每个记号代表一类单词(lexeme)。源程序中常见的记号可以归为几大类:关键字、标识符、字面量和特殊符号。词法分析器的输入是源程序,输出是识别的记号流。词法分析器的任务是把源文件的字符流转换成记号流。本质上它查看连续的字符然后把它们识别为“单词”。
2. 语法分析语法分析器根据语法规则识别出记号流中的结构(短语、句子),并构造一棵能够正确反映该结构的语法树。
3. 语义分析语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如果类型检查和转换等,其目的在于保证语法正确的结构在语义上也是合法的。
4. 中间代码生成中间代码生成器根据语义分析器的输出生成中间代码。中间代码可以有若干种形式,它们的共同特征是与具体机器无关。最常用的一种中间代码是三地址码,它的一种实现方式是四元式。三地址码的优点是便于阅读、便于优化。
5. 中间代码优化
优化是编译器的一个重要组成部分,由于编译器将源程序翻译成中间代码的工作是机械的、按固定模式进行的,因此,生成的中间代码往往在时间和空间上有很大浪费。当需要生成高效目标代码时,就必须进行优化。
6. 目标代码生成
目标代码生成是编译器的最后一个阶段。在生成目标代码时要考虑以下几个问题:计算机的系统结构、指令系统、寄存器的分配以及内存的组织等。编译器生成的目标程序代码可以有多种形式:汇编语言、可重定位二进制代码、内存形式。
7 符号表管理
符号表的作用是记录源程序中符号的必要信息,并加以合理组织,从而在编译器的各个阶段能对它们进行快速、准确的查找和操作。符号表中的某些内容甚至要保留到程序的运行阶段。
8 出错处理用户编写的源程序中往往会有一些错误,可分为静态错误和动态错误两类。所谓动态错误,是指源程序中的逻辑错误,它们发生在程序运行的时候,也被称作动态语义错误,如变量取值为零时作为除数,数组元素引用时下标出界等。静态错误又可分为语法错误和静态语义错误。语法错误是指有关语言结构上的错误,如单词拼写错、表达式中缺少操作数、begin和end不匹配等。静态语义错误是指分析源程序时可以发现的语言意义上的错误,如加法的两个操作数中一个是整型变量名,而另一个是数组名等。

2. c++高手求教 新手问题

不对吧,const静态类成员可以直接初始化,其他非const的静态类成员需要在类声明以外初始化。
http://www.cnblogs.com/fre2technic/archive/2011/03/25/1995044.html看一下这个博客,估计你就懂了

3. c/c++ 编译器如何区分char[] 与 char*

1、本身 char [] 和char * 就是不同的符号 编译器自然能识别 。
2、你说的是符号表吧。 符号表主要是给链接器用的。
3、关键你要理解C/C++程序的存储分布。
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于

数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据

结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态

变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统

释放。
4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区
这是一个前辈写的,非常详细
//main.cpp
int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b;栈
char s[]="abc"; //栈
char *p2; //栈
char *p3="123456"; //123456\0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1,"123456"); //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
}

stack:
由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1=(char*)malloc(10);
在C++中用new运算符
如p2=(char*)malloc(10);
但是注意p1、p2本身是在栈中的。

4. C语言程序编译后产生哪些类型的文件这些文件的作用是什么

1、以GCC编译器为例,可以分为四步。
第一步是预处理,包括语法检查等工作。
gcc
-P
abc.c
第二步由源程序生产汇编语言代码。
gcc
-S
abc.c
会生成abc.s文件,这个文件里就是汇编代码。
第三步编译器生成目标代码,一个源文件生成一个目标代码。
gcc
-c
abc.c
会生成abc.o
第四步连接器从目标代码生成可执行文件。
gcc
abc.o
2、目标代码包括机器码和符号表(函数及变量名)。连接器的主要作用是通过符号表在库文件和其他模块中找到在目标代码中引入或未定义的符号(函数及变量名),将几个目标代码合成可执行文件。

5. 关于C语言指针,打破砂锅问到底

程序编译阶段,编译器会生成一个符号表,里面记录的是每一个变量的相关信息,比如,类型,分配的地址等等。
编译的时候会查询符号表来编译,举个例子:
int *p;
假如符号表中记录的 p 所在的地址是1234;
p=&a;
当遇到这句的时候,编译器会查找符号表,找到符号a的信息,把它的地址记录下来(假设为4567),接着找到p的地址为1234,然后把上面那句话翻译为:*(int *)(1234)=4567;(意在说明问题,实际的产生的并非这样的代码,但原理是类似的)
,(int *)&&p,(int *)&&&p这些语句都是无效的。
想要深入了解,可以看看编译原理的符号表内容
希望对你有帮助

6. 定义语法树和符号表的数据结构

为了维持静态作用域的程序里各个名字的轨迹,编译器需要依靠一种称为符号表的数据结构。从最基本的层次上看,符号表就是一个字典:它把名字映射到编译器已知的有关信息。这里最基本的操作是把一个新映射关系(名字对象约束)放入表里,以及(非破坏性的)用一个给定名字去提取映射下的信息,以后我们把

这两个操作分别称为insert和lookup。大部分语言里的静态作用域规则还提出了另外的复杂性,它们要求在程序里的不同部分有不同的引用环境。为了处理作用域规则,我们可能希望简单增加一个remove操作。由于编译器在语义分析阶段要从头到尾扫描代码,这样它就可以在某个作用域开始时插入新约束,在作用域最后撤销它们。但是,存在一些因素使这种直接做法并不实际。

¨ 在许多有着嵌套作用域的语言里,内层声明的效果可以遮蔽外层声明,这就意味着符号表必须有能力为一个给定名字保存任意数目的映射。lookup操作必须返回最内层的映射,到作用域结束时还必须使外层映射重新变成可见的。

¨ 类Algol语言里的记录(结构)具有某种作用域性质,但却又不享有作用域那样的良好嵌套结构。当语义分析器看到一个记录声明时,它就必须记下各个记录域的名字(它们也是递归的,因为记录可以嵌套)。在这种声明结束时,各个域的名字又必须变成不可见的。然而,在此之后,一旦这一记录类型的某个变量出现在程序的正文里(例如在my_rec.field_name),在引用中位于圆点之后的部分,这些域名又必须立即重新变成可见的。在Pascal和另一些有with语句的语言里,记录域的名字还应该在多个语句的上下文里变成可见的。

¨ 某些时候一些名字有可能在它们被声明之前使用,即使在类Algol语言里情况也如此。举例说,Algol 60和Algol 68都允许标号的向前引用。Pascal避免了这种情况,它要求标号必须在作用域开始处声明,但还是允许指针声明的向前引用:

type

7. 西门子S7-300/400PLC的编程软件中,符号表的用处,和其导出导入的功能的作用。

符号表的用途在于给IO变量或数据定义一个名称,方便在编写程序及阅读程序的时候理解其含义。比如I0.0,定义为P0101_RUN,注释为提升泵运行信号,可以按自己熟悉的命名格式。其导入导出可以用EXCEL或其他工具快速定义符号。

变量表是用来监控相应变量在线状态的,可以根据不同的调试要求,生成多个变量表。变量表是不会下载到PLC里面的。

举个最简单的例子吧,控制一个阀门打开及关闭,有几个条件:I0.0 集中,I0.1打开,I0.2关闭,I0.3开到位,I0.4关到位,I0.5故障(比如过力距),Q0.0打开输出,Q0.1关闭输出。


(7)符号表在编译器中的作用扩展阅读:

美国汽车工业生产技术要求的发展促进了PLC的产生,20世纪60年代,美国通用汽车公司在对工厂生产线调整时,发现继电器、接触器控制系统修改难、体积大、噪声大、维护不方便以及可靠性差,于是提出了着名的“通用十条”招标指标。

1969年,美国数字化设备公司研制出第一台可编程控制器(PDP-14),在通用汽车公司的生产线上试用后,效果显着;

1971年,日本研制出第一台可编程控制器(DCS-8);1973年,德国研制出第一台可编程控制器;1974年,我国开始研制可编程控制器:1977年,我国在工业应用领域推广PLC。

最初的目的是替代机械开关装置(继电模块)。然而,自从1968年以来,PLC的功能逐渐代替了继电器控制板,现代PLC具有更多的功能。其用途从单一过程控制延伸到整个制造系统的控制和监测。

8. 如何建立符号表

Symbol Tables

为了维持静态作用域的程序里各个名字的轨迹,编译器需要依靠一种称为符号表的数据结构。从最基本的层次上看,符号表就是一个字典:它把名字映射到编译器已知的有关信息。这里最基本的操作是把一个新映射关系(名字对象约束)放入表里,以及(非破坏性的)用一个给定名字去提取映射下的信息,以后我们把

这两个操作分别称为insert和lookup。大部分语言里的静态作用域规则还提出了另外的复杂性,它们要求在程序里的不同部分有不同的引用环境。为了处理作用域规则,我们可能希望简单增加一个remove操作。由于编译器在语义分析阶段要从头到尾扫描代码,这样它就可以在某个作用域开始时插入新约束,在作用域最后撤销它们。但是,存在一些因素使这种直接做法并不实际。

¨ 在许多有着嵌套作用域的语言里,内层声明的效果可以遮蔽外层声明,这就意味着符号表必须有能力为一个给定名字保存任意数目的映射。lookup操作必须返回最内层的映射,到作用域结束时还必须使外层映射重新变成可见的。

¨ 类Algol语言里的记录(结构)具有某种作用域性质,但却又不享有作用域那样的良好嵌套结构。当语义分析器看到一个记录声明时,它就必须记下各个记录域的名字(它们也是递归的,因为记录可以嵌套)。在这种声明结束时,各个域的名字又必须变成不可见的。然而,在此之后,一旦这一记录类型的某个变量出现在程序的正文里(例如在my_rec.field_name),在引用中位于圆点之后的部分,这些域名又必须立即重新变成可见的。在Pascal和另一些有with语句的语言里,记录域的名字还应该在多个语句的上下文里变成可见的。

¨ 某些时候一些名字有可能在它们被声明之前使用,即使在类Algol语言里情况也如此。举例说,Algol 60和Algol 68都允许标号的向前引用。Pascal避免了这种情况,它要求标号必须在作用域开始处声明,但还是允许指针声明的向前引用:

type

company = record

CEO : ^person; (* forward reference *)

...

end;

person = record

employer : ^company;

...

end;

¨ Pascal和其他语言都允许子程序的向前声明,以便支持相互递归:

procere Q (A, B : integer); forward;

procere P (A, B : integer);

begin

...

Q (3, 4);

...

end;

procere Q; (* parameters are not repeated in Pascal *)

begin

...

P (4, 5);

...

end;

在看到这段代码里的向前声明时,语义分析器必须记住Q的参数,以便后面可以在Q的体里使它们重新变成可见的,但在此期间又必须使它们成为不可见的。这种操作类似于记住记录域的名字。

¨ 虽然我们有可能希望在作用域结束时忘记有关的名字,甚至回收这些名字在符号表里占据的空间,但有关它们的信息仍需要保存起来,以便符号纠错系统(symbolic debugger)使用。这种纠错系统是非常有用的工具,用户可以借助它方便地操纵程序,如启动程序,停住它,读出或者修改程序里的数据等等。为了分析来自用户的高级名字(例如,要求打印出my_firm^.revenues[1999] 的值),符号纠错程序必须能访问编译器的符号表。为了使符号表在运行时也可以用,编译器通常会把这个表保存到最后的机器语言程序里的某个隐蔽的部分。

静态作用域的大部分变化都可以通过扩充基本符号表的方式处理,通过增加一对enter_scope和leave_scope操作维持可见性的轨迹。任何东西都不会从符号表里删除,在整个编译阶段所有的结构都保留着,最后还要为纠错系统使用而保存起来。带有可见性处理的符号表可以以多种不同方式实现,下面描述的方式归功于LeBlanc和Cook [CL83]。

在遇到每个作用域时赋给它一个序列号。给定最外层的作用域(其中包含着预定义的标识符)编号0,包含用户定义全局名字的作用域给以编号1。其他作用域按遇到它们的顺序进行编号。所有的编号互不相同,它们并不表示词法嵌套的层次,但也有一点,嵌套于内部的子程序的编号自然会大于它们的外围作用域的编号。

所有的名字都被放入一个大的散列表里,以名字作为关键码,无论其作用域如何。表里的每项都包含一个符号名,其类属(变量、常量、类型、过程、域名字、参数等等),作用域编号,类型(一个指向另一符号表项的指针),以及另一些特定类属所拥有的信息。

除了这一散列表之外,符号表还包含一个作用域堆栈,它按顺序指明组成当前引用环境的所有作用域。在语义分析器扫描程序的过程中,在进入或退出程序时分别压入或者弹出这个堆栈。作用域堆栈的项里包含着作用域编号,指明这一作用域是否为闭的,有些情况下还可以有另外一些信息。

图3.13 LeBlanc-Cook符号表的lookup算法

当需要到表里查找名字时,我们会顺着某个适当的散列表链向下找,这样就会找到要找的名字所对应的一些项。对于每个匹配项,我们都向下扫描作用域堆栈,看看这个项所在的作用域是否可见。这种堆栈查看的深度不应超过最上面的闭作用域。要把导入项和导出项变为在它们的作用域之外可见的,方法就是在表里建立另外的项,让这些项里包含着指向实际项的指针。对于所有带有作用域编号0的项,我们都不需要去检查作用域堆栈,因为它们是渗透性的。图3.13里是lookup算法的伪代码。

图3.14的右下角是一个Mola-2程序的梗概,图中其余部分展现的是在过程P2里with语句处的引用环境的符号表配置情况。作用域堆栈里包含4个项,分别表示那个with语句,过程P2,模块M和全局作用域。with语句的作用域指明了在这一特定作用域里的(域)名字属于哪个记录变量。最外面的渗透性作用域没有显式表示。

图3.14 一个Mola-2例子程序的LeBlanc-Cook符号表。作用域堆栈表示在过程P2里with语句的引用环境。为清楚起见,许多指向符号表里对应于integer和real的项都用带括号的 (1) 和 (2) 表示,没有画出箭头。

因为这里的散列表以名字作为关键码,特定名字的所有项都出现在同一个散列链里。在这个例子里,散列冲突导致A2、F2和T出现在同一个链里。变量V和I(M的I)有另外的项,使它们跨过闭作用域M的边界后仍为可见的。当我们处于P2里时,对于I的查找操作将找到P2的I,M的I里的两个项都不可见。类型T的项指明了在with语句期间放入作用域堆栈的作用域编号。每个子程序的项里包含了一个头指针,指向子程序参数的链接表,以便做调用分析时使用(图中没有给出这些链的一些链接)。在代码生成过程中,许多符号表项还可能包含另外的域,表示例如对象大小和运行时地址等等信息。

图片信息,看参考资料。。。

9. 符号表和抽象语法树是什么关系两者在编译器设计中是否必需

一般的编译器可能包含下面这些模块:

1, 词法分析器:
输入: 源代码
输出: token
2, 语法分析器:
输入: token
输出: AST
在这个过程中, 可以识别出不符合语法规则的语句, 就可以报syntax错误, 如果有syntax错误, 编译结束
3, 语义分析器:
输入: AST
输出: 无
在这个过程中, 根据语言的语义规则来识别语义错误, 要识别语义错误 就必须编译AST, 因为是树的遍历, 假如你先遍历到了int a 这个节点, 接着又遍历到了一个表达式a = 4这个节点, 你需要检查变量a有没有声明啊, 变量a和4的类型批不匹配呢? 这时你如果没有保存变量a的信息, 那么你怎么检查? 所以就需要符号表来保存这些信息了.
4, 代码优化:
最简单的就是常量折叠优化了, 比如: a = 1 + 2 这个语句可以直接换成: a = 3了, 也就是说在编译阶段就把一些必要的运算先计算完成, 在程序运行的时候就不需要计算这些了, 就提高了程序的运行效率. 这部分是最复杂的了, 还有各种各样各样的优化
5, 代码生成:
输入: AST
输出: 可以是虚拟机代码, 可以是本地汇编代码

阅读全文

与符号表在编译器中的作用相关的资料

热点内容
文件夹无限制压缩会不会降低内存 浏览:408
荣耀怎样创建文件夹 浏览:625
如何用本机登陆远程服务器地址 浏览:680
黄小鸭解压文具盒 浏览:670
女程序员的转行方法 浏览:881
东风启辰车联网安装文件夹 浏览:524
华为怎么设置app时间锁 浏览:660
后宫app视频怎么下载 浏览:525
如何把图片转换从PDF格式 浏览:259
重写和重载的区别java 浏览:234
expressvpnandroid 浏览:84
储存卡被加密怎么解除 浏览:169
地球怎么压缩直径 浏览:780
金铲铲之战服务器爆满怎么进 浏览:160
同仁堂pdf 浏览:935
如何编译原理课程教材 浏览:730
单片机控制显示器 浏览:776
顶好花app下载怎么找不到 浏览:989
手机命令大全 浏览:808
怎么下邮政银行app 浏览:250