最简单的c程序gcc源码编译流程分析



第1章 c代码“int a;”语法分析

GCC如何解析c代码“int a;”函数c_parse_file()C语法分析的入口函数,我们从此函数开始来分析一段简单的c代码是如何从产生式(推导式、文法)来推到出来的。

gcc源码中大多函数之前都有一段对应的产生式,通过这个产生式能够明白对应的函数是怎么实现的、实现了什么功能。(俞峰老师强调一定要看懂产生式在看函数的实现)。

1.1 入口函数c_parse_file

C语法分析的入口函数源码:

/* Parse a single source file.  */

void

c_parse_file (void)

{

……

 

  c_parser_translation_unit (the_parser);

  the_parser= NULL;

}

1.2 翻译单元解析函数c_parser_translation_unit

函数c_parser_translation_unit (the_parser)用来解析翻译单元。什么是翻译单元呢?

语言叙述:

翻译单元(translation-unit可以推导出多个外部声明external-declarations

那什么是外部声明呢?多个外部声明external-declarations)可以推导出一个外部声明external-declaration)或者多个外部声明external-declarations)和一个外部声明external-declaration)。(由于中文翻译的歧义,请看形式化表示。)

形式化表示:

translation-unitexternal-declarations

external-declarationsexternal-declaration

external-declarationsexternal-declarationsexternal-declaration

也就是说如果将translation-unit S表示,将external-declarationsA表示,将external-declarationB表示;SAABAAB。那么S可以推导出由任意非0B组成的单元,例如SBSBBSBBB……

那么我们由此来分析翻译单元是如何推导出“int a;”的,容易想到“int a; 仅仅是一条声明,那么选择translation-unitexternal-declarations→external-declaration

还需要研究源码,gcc怎么知道是一条声明的,如何选择分支的?

源码:

/* Parse a translation unit (C90 6.7, C99 6.9).

 

   translation-unit://翻译单元

    external-declarations

 

   external-declarations:

     external-declaration

     external-declarations external-declaration

 

   GNUextensions://GUN扩展语法

 

  translation-unit:

     empty

*/

 

static void

c_parser_translation_unit (c_parser *parser)

{

……

      do

            {

              ggc_collect ();

              c_parser_external_declaration (parser);

              obstack_free (&parser_obstack,obstack_position);

            }

      while(c_parser_next_token_is_not (parser, CPP_EOF));

……

}

1.3 外部声明解析函数c_parser_external_declaration

3.2中提到的对多的就是外部声明external_declaration,那么什么是外部声明呢,当然外部声明也是文法组成的,它不是终结符。

注:终结符,通俗的说就是不能单独出现在推导式左边的符号,也就是说终结符不能再进行推导。不是终结符的都是非终结符。非终结符可理解为一个可拆分元素,而终结符是不可拆分的最小元素。如:有α→β ,则α 必然是个非终结符。

如果我们知道了外部声明(external_declaration)的文法,我们就明白什么是外部声明external_declaration)。那么c_parser_external_declaration就是来处理外部声明的函数,当然这个函数由3.2中函数c_parser_translation_unit来调用,详见3.2c_parser_translation_unit源码。

external declaration文法源码:

/* Parse an external declaration (C90 6.7, C996.9).

 

   external-declaration:

    function-definition

     declaration

 

   GNU extensions:

 

  external-declaration:

    asm-definition

     ;

    __extension__ external-declaration

 

   Objective-C:

 

  external-declaration:

    objc-class-definition

    objc-class-declaration

    objc-alias-declaration

    objc-protocol-definition

     objc-method-definition

     @end

*/

external-declaration文法形式化表述:

external-declarationfunction-definition                       

external-declarationdeclaration                            

(注:为简化起见GNU extensionsObjective-C部分未做分析)

由此看出外部声明(external-declaration)可以推导出函数定义(function-definition)或者声明(declaration);

继续按照3.2的方式推导c代码“int a; translation-unitexternal-declarations→external-declarationdeclaration

那么declaration是有谁解析的呢?declaration的文法是什么呢?通过函数c_parser_external_declaration()的源代码我们发现此函数调用了函数c_parser_declaration_or_fndef(),gcc正是通过c_parser_declaration_or_fndef()来解析declaration的。

c_parser_external_declaration源码:

static void

c_parser_external_declaration(c_parser *parser)

{

……

      c_parser_declaration_or_fndef (parser,true, true, true, false, true,NULL, vNULL);

      break;

    }

}

1.4 解析声明和函数定义函数c_parser_declaration_or_fndef

下面我们来分析函数c_parser_declaration_or_fndef是怎么实现的。同理先理解此函数的产生式。这里为只分析“int a;”所以我们按照上述的translation-unitexternal-declarations→external-declarationdeclaration继续推导,我们分析declaration的描述;从下文c_parser_declaration_or_fndef产生式源码中我们可以得到declaration的相关描述。

c_parser_declaration_or_fndef产生式源码:

   declaration:

     declaration-specifiersinit-declarator-list[opt] ;

     static_assert-declaration

 

  function-definition:

     declaration-specifiers[opt]declarator declaration-list[opt]

      compound-statement

 

  declaration-list:

    declaration

    declaration-list declaration

 

   init-declarator-list:

     init-declarator

     init-declarator-list , init-declarator

 

  init-declarator:

    declarator simple-asm-expr[opt] attributes[opt]

    declarator simple-asm-expr[opt] attributes[opt] = initializer

形式化表述:

  1. declarationdeclaration-specifiers init-declarator-list[opt] ;

  2. declarationstatic_assert-declaration

  3. init-declarator-listinit-declarator

  4. init-declarator-listinit-declarator-list , init-declarator

  1. init-declaratordeclaratorsimple-asm-expr[opt]attributes[opt]

  1. init-declaratordeclarator simple-asm-expr[opt] attributes[opt] = initializer

注意:上述分号是推导式(文法)的一部分。

这里我们能够得到一条声明(declaration)能够推导出声明符(declaration-specifiers 初始化说明符(init-declarator-list[opt])(这里的opt意思是可选的意思。)和一个“;”分号。到此不难得出“int a;”的推导式为

translation-unitexternal-declarationsexternal-declarationdeclarationdeclaration-specifiers init-declarator-list                           1-1

到此“int a;”中的分号就被从文法推导式的角度推导出来了。

按照gcc文法的规则,它将自左向右依次解析符号,直到解析到终结符为止,分号“;”即为一个终结符。由于这里c_parser_declaration_or_fndef函数给出了init-declarator-list的文法,我们先分析init-declarator-list,稍后补充上declaration-specifiers;值得注意的是gcc文法规则是自左向右先分析元素declaration-specifiers的。

由于“int a;”中声明的的变量只有a一个符号,所以不难推导此时的文法推导为3.init-declarator-listinit-declarator 。如果解析C代码为“int a,b;”这时的文法推导就是上述4.init-declarator-list→init-declarator-list , init-declarator。很明显这个4.式有个逗号“,”分隔,可以解析形如“int a,b,c;”“int a,b,c,d;”等C源码。

至此,我们推导出(1-1)中的init-declarator-list

init-declarator-listinit-declarator                            (式1-2

又由5.式不难得到可以得到:

init-declarator-listinit-declaratordeclarator                   (式1-3

c_parser_declaration_or_fndef源码:

static void

c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,

                                          bool static_assert_ok, bool empty_ok,

                                          bool nested, bool start_attr_ok,

                                          tree *objc_foreach_object_declaration,

                                          vec<c_token>omp_declare_simd_clauses)

{

 ……

 

  c_parser_declspecs(parser, specs, true, true, start_attr_ok,

                             true, true, cla_nonabstract_decl);

while (true)

    {

     ……

      declarator = c_parser_declarator (parser,

                                                          specs->typespec_kind!= ctsk_none,

                                                          C_DTR_NORMAL,&dummy);

      if (declarator == NULL)

……

我们继续分析declarator的文法,我们可以通过函数c_parser_declaration_or_fndef()中调用的实现文法declarator的函数c_parser_declspecs ()来定位到文法declarator

1.5 标识符解析函数c_parser_declarator

declarator文法源码:

   declarator:

     pointer[opt] direct-declarator

 

   direct-declarator:

     identifier

     ( attributes[opt] declarator )

     direct-declarator array-declarator

     direct-declarator ( parameter-type-list )

     direct-declarator ( identifier-list[opt] )

形式化定义:

declaratorpointer[opt] direct-declarator

direct-declaratoridentifier

direct-declaratordirect-declarator array-declarator

direct-declaratordirect-declarator( parameter-type-list )

direct-declaratordirect-declarator( identifier-list[opt] )

由于pointer是可选元素,那么容易推导出:

declarator→direct-declarator→identifier                         (式1-4

这里的识别符identifier就是一个终结符,在源码“int a;”中指的就是a这个标识符。由(1-3)、(1-4)得到:

init-declarator-listinit-declaratordeclaratordirect-declaratoridentifier

(式1-5

1.6 声明符解析函数c_parser_declspecs

部分文法源码:

   declaration-specifiers:

     storage-class-specifierdeclaration-specifiers[opt]

     type-specifier declaration-specifiers[opt]

     type-qualifier declaration-specifiers[opt]

     function-specifierdeclaration-specifiers[opt]

     alignment-specifier declaration-specifiers[opt]

 C90 6.5.2, C99 6.7.2:

   type-specifier:

     void

     char

     short

     int

     long

     float

     double

     signed

     unsigned

     _Bool

     _Complex

通过文法不难推导出“int a;”的declaration-specifiers的文法推导为:

declaration-specifierstype-specifierint                        (1-6)

由(式1-1)、(式1-5)和(1-6)可得到“int a;”的文法推导树如下图所示:

                

至此,我们完成了最简单的c语言代码“inta;”的文法推导,只有在理解gcc文法的基础上在去分析对应的函数才能理清思路。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值