gcc编译器前端简要分析

GCC编译器内部:词法分析与语法解析详解
本文详细介绍了GCC编译器中的词法分析过程,如何将源代码转换为词法符号,并构建AST节点。同时揭示了语法分析的关键函数c_parse_file和c_parser_translation_unit的作用,以及它们在C语言编译流程中的角色。

参考

《gcc源码分析》

词法分析

词法分析的过程就是将源代码识别成一个一个的词法符号,并在词法分析的过程中创建一些树节点,用来保存某些词法符号的值(value)。这些需要创建树节点的词法符号主要包括标识符(CPP_NAME)、关键字(CPP_KEYWORD)、数值(CPP_NUMBER)常量等。在后续的语法分析过程中,会将这些AST节点(或者AST子树)根据语法规则链接起来,形成某个函数完整的AST结构。

语法分析

语法分析就是,提前预读两个词法符号的自顶向下的语法推导过程:包括提前预读词法符号,语法分析状态以及上下文信息。

这些信息通常将保存在c_parser 这个结构体当中:

/* A parser structure recording information about the state and
   context of parsing.  Includes lexer information with up to two
   tokens of look-ahead; more are not needed for C.  */
typedef struct c_parser GTY(())
{
  /* The look-ahead tokens.  */
  c_token tokens[2];
  /* How many look-ahead tokens are available (0, 1 or 2).  */
  short tokens_avail;
  /* True if a syntax error is being recovered from; false otherwise.
     c_parser_error sets this flag.  It should clear this flag when
     enough tokens have been consumed to recover from the error.  */
  BOOL_BITFIELD error : 1;
  /* True if we're processing a pragma, and shouldn't automatically
     consume CPP_PRAGMA_EOL.  */
  BOOL_BITFIELD in_pragma : 1;
  /* True if we're parsing the outermost block of an if statement.  */
  BOOL_BITFIELD in_if_block : 1;
  /* True if we want to lex an untranslated string.  */
  BOOL_BITFIELD lex_untranslated_string : 1;
  /* Objective-C specific parser/lexer information.  */
  BOOL_BITFIELD objc_pq_context : 1;
  /* The following flag is needed to contextualize Objective-C lexical
     analysis.  In some cases (e.g., 'int NSObject;'), it is
     undesirable to bind an identifier to an Objective-C class, even
     if a class with that name exists.  */
  BOOL_BITFIELD objc_need_raw_identifier : 1;
} c_parser;

gcc对c语言进行语法分析的入口函数为c_parse_file :


/* Parse a single source file.  */

void
c_parse_file (void)
{
  /* Use local storage to begin.  If the first token is a pragma, parse it.
     If it is #pragma GCC pch_preprocess, then this will load a PCH file
     which will cause garbage collection.  */
  c_parser tparser;

  memset (&tparser, 0, sizeof tparser);
  the_parser = &tparser;

  if (c_parser_peek_token (&tparser)->pragma_kind == PRAGMA_GCC_PCH_PREPROCESS)
    c_parser_pragma_pch_preprocess (&tparser);

  the_parser = GGC_NEW (c_parser);
  *the_parser = tparser;

  c_parser_translation_unit (the_parser);
  the_parser = NULL;
}

预读一个符号

预读一个符号,如果是PRAGMA_GCC_PCH_PREPROCESS,将进行编译的预处理工作,c_parser_pragma_pch_preprocess 为预处理函数。
c_parser_peek_token 会解析出一个token,这时会返回指向下个token的指针:

/* Return a pointer to the next token from PARSER, reading it in if
   necessary.  */

static inline c_token *
c_parser_peek_token (c_parser *parser)
{
  if (parser->tokens_avail == 0)
    {
      c_lex_one_token (parser, &parser->tokens[0]);
      parser->tokens_avail = 1;
    }
  return &parser->tokens[0];
}

解析一个单独的词法符号时,调用c_lex_one_token

/* Read in and lex a single token, storing it in *TOKEN.  */

static void
c_lex_one_token (c_parser *parser, c_token *token)
{
  timevar_push (TV_LEX);

  token->type = c_lex_with_flags (&token->value, &token->location, NULL,
				  (parser->lex_untranslated_string
				   ? C_LEX_STRING_NO_TRANSLATE : 0));
  token->id_kind = C_ID_NONE;
  token->keyword = RID_MAX;
  token->pragma_kind = PRAGMA_NONE;

  switch (token->type)
    {
    case CPP_NAME:
      {
	tree decl;

	bool objc_force_identifier = parser->objc_need_raw_identifier;
	if (c_dialect_objc ())
	  parser->objc_need_raw_identifier = false;

	if (C_IS_RESERVED_WORD (token->value))
	  {
	    enum rid rid_code = C_RID_CODE (token->value);

	    if (rid_code == RID_CXX_COMPAT_WARN)
	      {
		warning_at (token->location,
			    OPT_Wc___compat,
			    "identifier %qs conflicts with C++ keyword",
			    IDENTIFIER_POINTER (token->value));
	      }
	    else if (c_dialect_objc ())
	      {
		if (!objc_is_reserved_word (token->value)
		    && (!OBJC_IS_PQ_KEYWORD (rid_code)
			|| parser->objc_pq_context))
		  {
		    /* Return the canonical spelling for this keyword.  */
		    token->value = ridpointers[(int) rid_code];
		    token->type = CPP_KEYWORD;
		    token->keyword = rid_code;
		    break;
		  }
	      }
	    else
	      {
		token->type = CPP_KEYWORD;
		token->keyword = rid_code;
		break;
	      }
	  }

	decl = lookup_name (token->value);
	if (decl)
	  {
	    if (TREE_CODE (decl) == TYPE_DECL)
	      {
		token->id_kind = C_ID_TYPENAME;
		break;
	      }
	  }
	else if (c_dialect_objc ())
	  {
	    tree objc_interface_decl = objc_is_class_name (token->value);
	    /* Objective-C class names are in the same namespace as
	       variables and typedefs, and hence are shadowed by local
	       declarations.  */
	    if (objc_interface_decl
		&& (global_bindings_p ()
		    || (!objc_force_identifier && !decl)))
	      {
		token->value = objc_interface_decl;
		token->id_kind = C_ID_CLASSNAME;
		break;
	      }
	  }
        token->id_kind = C_ID_ID;
      }
      break;
    case CPP_AT_NAME:
      /* This only happens in Objective-C; it must be a keyword.  */
      token->type = CPP_KEYWORD;
      token->keyword = C_RID_CODE (token->value);
      break;
    case CPP_COLON:
    case CPP_COMMA:
    case CPP_CLOSE_PAREN:
    case CPP_SEMICOLON:
      /* These tokens may affect the interpretation of any identifiers
	 following, if doing Objective-C.  */
      if (c_dialect_objc ())
	parser->objc_need_raw_identifier = false;
      break;
    case CPP_PRAGMA:
      /* We smuggled the cpp_token->u.pragma value in an INTEGER_CST.  */
      token->pragma_kind = (enum pragma_kind) TREE_INT_CST_LOW (token->value);
      token->value = NULL;
      break;
    default:
      break;
    }
  timevar_pop (TV_LEX);
}

语法分析函数调用关系

在这里插入图片描述

重点函数分析

c_parse_file

函数c_parse_filer如上所述,是C语法分析的入口函数,该函数首先对当前的词法符号进行判断,如果该符号的编译制导类型是PRAGMA_GCC_PCH_PREPROCESS,则调用函数c_parser_pragma_pch_preprocess对进行源代码的预处理,否则,调用函数c_parser_translation_unit()进行语法推导。

c_parser_translation_unit

这个函数是进行语法分析的主流程函数:

<think>嗯,用户想了解GCC编译器的使用说明、特性、配置和示例。首先,我需要整理GCC的基本信息。GCC是GNU Compiler Collection,支持多种编程语言,比如C、C++、Fortran等。它的特性包括跨平台、多语言支持、优化选项和插件扩展。这些信息应该放在回答的开头部分。 接下来是使用说明。用户可能需要知道编译的基本流程,比如预处理、编译、汇编、链接。需要分步骤解释,每个阶段对应的GCC选项是什么。比如预处理用-E,编译用-S,汇编用-c,最后链接生成可执行文件。这部分可以结合示例代码,比如一个简单的hello.c文件,展示如何用gcc -o hello hello.c来编译。 然后是关于常用编译选项。用户提到的参考资料中有提到-O优化选项、-g调试选项、-l链接库、-L指定库路径等。需要把这些选项分类整理,比如优化选项、调试选项、警告选项、链接选项,并给出每个选项的示例用法,比如-O2用于优化,-Wall开启所有警告。 配置方面,可能需要说明如何设置环境变量,比如CC和CFLAGS,或者使用Makefile来管理项目。这部分需要简洁明了,给出具体的例子,比如在Makefile中定义编译规则,或者通过命令行传递参数。 示例部分,用户可能需要看到实际的编译命令和结果。比如编译单个文件、多文件项目,制作和使用静态库、动态库。这些示例需要步骤详细,比如如何生成.o文件,如何打包成静态库,如何编译动态库并链接。例如,静态库使用ar rcs,动态库使用-shared -fPIC,然后链接时用-l和-L指定。 用户提供的引用资料中提到了使用-v查看详细编译过程,这个可以加到调试或高级选项里。另外,参考资料中的优化级别-O和调试选项-g也需要强调,说明它们的作用和适用场景。 可能需要考虑用户的实际应用场景,比如嵌入式系统开发,这时候可以提到交叉编译相关的选项,比如--target,但用户的问题没有特别提到嵌入式,所以可能放在特性或配置部分简要说明。 还要注意用户提到的“其他类似编译器”,比如Clang,但用户主要问的是GCC,所以可能不需要展开,除非在比较中提及。不过用户的问题明确是关于GCC,所以重点放在GCC上。 需要检查是否有遗漏的重要选项,比如-std指定标准,-I包含头文件路径,这些在项目配置中常用,应该包含进去。还有处理警告的选项,如-Werror将警告视为错误,这对严格的项目有用。 最后,生成相关问题时要围绕GCC的使用、优化、调试、配置、对比其他编译器等方面提问,确保问题有针对性,帮助用户进一步学习。 总结回答结构:先介绍GCC,然后分特性、使用说明(包括编译流程和常用选项)、配置方法、示例(单文件、多文件、库),最后相关问题。确保每个部分有示例代码或命令,并正确引用提供的资料,比如优化选项-O和调试-g来自引用3,-l来自引用1,编译过程步骤来自引用2。</think># GCC编译器使用指南 ## 一、基本介绍 GNU Compiler Collection(GCC)是支持多种编程语言编译器套件,包含C、C++、Objective-C、Fortran、Ada等前端。最新版本支持C++23标准,并持续优化编译性能[^1]。 ## 二、核心特性 1. **跨平台支持**:可在x86、ARM、RISC-V等架构运行 2. **多语言支持**:提供C/C++/Fortran等多语言前端 3. **优化能力**:支持-O0到-O3四级优化,包含-Os(空间优化)、-Og(调试优化) 4. **插件扩展**:支持自定义编译插件 5. **交叉编译**:支持通过--target指定目标平台[^3] ## 三、编译流程 ```bash # 查看完整编译过程(包含预处理/编译/汇编/链接) gcc -o hello hello.c -v ``` 1. **预处理**:`gcc -E hello.c -o hello.i` - 处理宏定义和文件包含 2. **编译**:`gcc -S hello.i -o hello.s` - 生成汇编代码 3. **汇编**:`gcc -c hello.s -o hello.o` - 生成目标文件 4. **链接**:`gcc hello.o -o hello`[^2] ## 四、常用编译选项 | 类别 | 选项 | 说明 | 示例 | |------------|--------------------|--------------------------------|---------------------------| | 优化选项 | -O0/-O1/-O2/-O3 | 优化级别 | `gcc -O2 main.c` | | 调试选项 | -g | 生成调试信息 | `gcc -g test.c` | | 警告选项 | -Wall/-Wextra | 开启所有警告 | `gcc -Wall program.c` | | 标准指定 | -std=c11 | 指定C语言标准 | `gcc -std=c99 demo.c` | | 链接选项 | -l<name> -L<path> | 链接库文件 | `gcc -lm -L/lib math.c` | | 预处理 | -D<MACRO> | 定义宏 | `gcc -DDEBUG version.c` | ## 五、项目配置示例 ### 1. 多文件编译 ```bash gcc -c utils.c # 生成utils.o gcc -c main.c # 生成main.o gcc main.o utils.o -o app ``` ### 2. 静态库制作 ```bash gcc -c libmath.c ar rcs libmath.a libmath.o gcc main.c -L. -lmath -o static_demo ``` ### 3. 动态库编译 ```bash gcc -fPIC -shared math.c -o libmath.so gcc main.c -L. -lmath -Wl,-rpath=. -o dynamic_demo ``` ## 六、高级配置技巧 1. **环境变量配置**: ```bash export CFLAGS="-O2 -Wall" export LDFLAGS="-L/usr/local/lib" ``` 2. **Makefile示例**: ```makefile CC = gcc CFLAGS = -O2 -Wall OBJS = main.o utils.o app: $(OBJS) $(CC) -o $@ $^ ``` 3. **调试信息增强**: ```bash gcc -g3 -O0 -fno-inline test.c # 保留所有调试符号 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值