C编译器剖析_3.3 语法分析_C语言的外部声明(1)

3.3  C语言的外部声明_ExternalDeclaration

    在分析完表达式和语句之后,《K&R》附录中关于标准C的6页文法,我们就已经差不多已经理解了一半,另外的3页文法基本上都是关于声明Declaration的,与声明相关的代码在ucl\decl.c中。不少编程语言的书籍中,对声明与定义是有严格区分的,在编译器遇到定义时,需要为之分配内存;而遇到声明时,则不必为之分配内存,因为该声明对应的内存可能已在其他地方进行过分配。例如,以下代码中,”extern int a;”被当作对变量a的声明;而”int b = 5;”则被当作定义,需要为变量b分配存储空间,并作相应初始化;”void f(void);”被当作对函数的声明,不需要占任何代码区空间;而函数g则是定义,需要在代码区中占用一片内存来存放函数代码。

         extern int a;

         int b = 5;

         void f(void);

         void g(void){

                   //…..

         }

     不过,从C标准文法的角度出发,上述“需要占内存的定义definition”或者“不占内存的声明”统统是被当作“外部声明”ExternalDeclaration来处理的。是否需要占内存,其实已经涉及“语义”。从C语言的语义出发,上述外部声明才有“是否要占内存”之分。换句话说,在语法分析阶段,我们暂时只在乎C程序在形式上是否符合C标准文法,我们在这个阶段只管“形”,而不管“含义”或“内容”。在语法分析阶段,一个预处理后的C文件就对应一个翻译单元TranslationUnit,它由若干个外部声明ExternalDeclaration构成,如图3.3.1第1415至1417行的产生式所示。

 

图3.3.1  ParseTranslationUnit()

     图3.3.1第1428行的函数调用ReadSourceFile()用于读入预处理后的C文件,第1430和1431行用于初始化当前读头的坐标位置,第1432行创建的向量TypedefNames用于存放通过typedef关键字命名的类型名,而接下来的OverloadNames则用于记录“另作他用”的typedef类型名,我们在3.1节时举例介绍过这两个向量的用法。第1435行创建了一个kind域为NK_TranslationUnit的语法树结点,对外部声明ExternalDeclaration的分析主要是通过第1450行的函数调用ParseExternalDeclaration()来实现的。而一个翻译单元是由若干个外部声明构成的,所以我们需要第1443行的while循环。引入第1446至1449行的代码,主要是为了允许形如”;”这样的空语句。第1436行、1450行和1451行的代码则用于把各个外部声明所对应的语法树结点链接到一起。执行到第1460行,我们就已经完成了对一个预处理后的C文件的语法分析工作,通过CloseSourceFile()关闭相关文件即可。图3.3.2给出了一个预处理后的C文件及其对应的语法树示意图。   

    

图3.3.2   翻译单元TranslationUnit对应的语法树

     图3.3.2中,我们通过在while()循环中调用函数ParseExternalDeclaration(),依次创建了kind域为NK_Declaration或NK_Function的4个语法树结点,其中NK_Function对应的是structastFunction结构体对象,描述了函数定义g的相关信息;而NK_Declaration对应的是structastDeclaration结构体对象,描述了变量声明a、变量定义b和函数声明f。而用于分析“外部声明”的相关代码如图3.3.3所示。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值