您的位置:首页 > 其它

C编译器剖析_4.1 语义检查_语义检查简介

2015-02-11 16:54 309 查看
4.1 语义检查简介
在这一章中,我们需要在语法分析阶段建立的语法树的基础上,进行语义检查。UCC编译器中与此相关的代码主要在ucl\declchk.c,ucl\stmtchk.c和ucl\exprchk.c,分别用于对声明Declaration、语句Statement和表达式Expression进行语义检查SemanticsCheck。其中,最重要的工作就是建立C语言的类型系统,我们在第2.4节时就已简单介绍过C语言的类型系统。在阅读这部分代码时,一定需要结合语法树进行,看起来比较笨但却很管用的办法是,把第3章中我们构造出来的语法树画在纸质的笔记本上,对照着语法树,才不至于迷失方向。我们就如一只猴子,在语法树上跳来跳去进行语义检查。在如此枝繁叶茂的语法树上,如果没有对应的图纸做导航,很快就会“云深不知处”了。
按UCC编译器的正常执行流程,我们需要先为变量名和函数名建立起类型信息,这部分工作就主要是在declchk.c中进行,之后再进行语句和表达式的语义检查。我们反复强调过,通过声明Declaration,C程序员实际上建立了类型表达式,UCC编译器会在语义检查时,为变量名和函数名等标志符建立起相应的类型结构。在第2.4节时,我们已初步介绍了在UCC编译器中,整数、函数、结构体、数组和指针等类型结构在UCC编译器内部是如何表示的。而ucl\declchk.c中的代码就是用于创建这些类型结构。有了这些类型信息后,我们就可以进一步检查一下表达式Expression和语句Statement在语义上是否正确,如图4.1.1所示。其中,a是个结构体对象,而b是个整数,按C语言的语义,表达式a+b是非法的。对表达式的语义检查工作是在ucl\exprchk.c中进行的,第16行的错误提示告诉我们这个错误是在exprchk.c第886行发现的。而第17行的错误提示告诉我们,第10行的case语句没有出现在switch语句,这个错误是在stmtchk.c的第146行发现的。类似的,在构建类型系统时,UCC编译器也会进行检查,第15行给出的错误提示则说明,第7行声明的d与之前声明的类型不兼容,在第6行中d被声明为double,而在第7行又被声明为int,这个错误是在declchk.c的第1587行检测到的。



图4.1.1 语义检查的例子
阅读UCC编译器语义检查相关代码的一个小诀窍就是,故意编写一些形如图4.1.1的有语义错误的C代码,上机运行后,看看UCC编译器是在哪里进行报错的,又是如何发现这个错误的。相对而言,declchk.c中的代码比较复杂。在进行代码分析时,我们还是暂时忽略“如何构建2.4节各图所示的类型结构”这样的问题,即先不去分析declchk.c中的代码。我们假设形如2.4节的类型结构已经构建完毕,在此基础上,我们先来对表达式进行语义检查,即先讨论exprchk.c中的代码。这种分析策略的好处是,我们可以在对表达式进行语义检查时,进一步熟悉C语言的类型系统。而对语句的语义检查,则相对会简单许多,看看stmtchk.c中的代码行数就可略知一二。最后,我们再回到语义检查的重点戏“类型系统的建立”,即declchk.c。简单而言,分析的顺序如下所示:

exprchk.c --- > stmtchk.c --- > declchk.c

这样的分析次序违背UCC编译器的执行顺序,但是应比较符合“先易后难,先感性后理性”的理解和认知顺序。当然,这需要我们有“暂时囫囵吞枣和不求甚解”的洒脱,有了2.4节的基础,我们可以暂时搁置“如何建立类型系统”这样的拦路虎,而是先绕开它,直扑表达式和语句。如此迂回的目的不是为了避开它,而是为了更好地消灭它。其实,回头翻看第3章的目录,就会发现其实我们在语法分析时,也是按照这样的次序来分析的。先使用类型系统来进行表达式的语义检查,然后再思考类型系统是如何创建的,这应比较符合大部分人的使用习惯。就如我们是先上机用C编译器来学C语言,到一定火候后,我们才会在这边讨论C编译器是如何构建的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: