GCC-3.4.6源代码学习笔记(34)

来源:互联网 发布:档案数字化软件 编辑:程序博客网 时间:2024/05/22 05:06

4.1.3.1.2.1.4.              读入宏定义

在剩下的PCH文件中,包含了其宏定义的细节。根据这个信息,现在我们可以把这些定义插入cpp_reader里。在PCH文件中,每个宏定义以以下的macrodef_struct结构开头。

 

38    struct macrodef_struct                                                                                in cpppch.c

39    {

40      unsigned int definition_length;

41      unsigned short name_length;

42      unsigned short flags;

43    };

 

注意上面的name_length亦包含在definition_length中。

 

cpp_read_state (continue)

 

654      old_state = r->state;

655   

656      r->state.in_directive = 1;

657      r->state.prevent_expansion = 1;

658      r->state.angled_headers = 0;

659   

660     /* Read in the identifiers that must be defined.  */

661      for (;;)

662      {

663        cpp_hashnode *h;

664         

665        if (fread (&m, sizeof (m), 1, f) != 1)

666          goto error;

667         

668        if (m.name_length == 0)

669          break;

670   

671        if (defnlen < m.definition_length + 1)

672        {

673          defnlen = m.definition_length + 256;

674          defn = xrealloc (defn, defnlen);

675        }

676   

677        if (fread (defn, 1, m.definition_length, f) != m.definition_length)

678          goto error;

679        defn[m.definition_length] = '/n';

680         

681        h = cpp_lookup (r, defn, m.name_length);

682   

683        if (h->type == NT_MACRO)

684          _cpp_free_definition (h);

685        if (m.flags & NODE_POISONED)

686          h->flags |= NODE_POISONED | NODE_DIAGNOSTIC;

687        else if (m.name_length != m.definition_length)

688        {

689          if (cpp_push_buffer (r, defn + m.name_length,

690                           m.definition_length - m.name_length, true)

691                               != NULL)

692          {

693            _cpp_clean_line (r);

694            if (!_cpp_create_definition (r, h))

695              abort ();

696            _cpp_pop_buffer (r);

697          }

698          else

699            abort ();

700        }

701      }

702   

703      r->state = old_state;

704      r->line = saved_line;

705      free (defn);

706      defn = NULL;

707   

708      if (deps_restore (r->deps, f, CPP_OPTION (r, restore_pch_deps) ? name : NULL)

709          != 0)

710        goto error;

711    

712      return 0;

713     

714    error:

715      cpp_errno (r, CPP_DL_ERROR, "while reading precompiled header");

716      return -1;

717    }

 

上面683行,如果该宏已经被定义,这个旧的定义应该被覆盖。函数cpp_free_definition首先清除这个定义。

 

1227 void

1228 _cpp_free_definition (cpp_hashnode *h)                                               in cppmacro.c

1229 {

1230   /* Macros and assertions no longer have anything to free.  */

1231   h->type = NT_VOID;

1232   /* Clear builtin flag in case of redefinition.  */

1233   h->flags &= ~(NODE_BUILTIN | NODE_DISABLED);

1234 }

4.1.3.1.2.1.4.1.        准备缓存

638行,如果name_length不等于definition_length,表示后跟定义体。那么首先要为后面的处理准备缓存。看到cpp_reader中的buffer_ob具有obstack类型——它自己管理内存。

 

1928 cpp_buffer *

1929 cpp_push_buffer (cpp_reader *pfile, const uchar *buffer, size_t len,             in cpplib.c

1930       int from_stage3)

1931   {

1932     cpp_buffer *new = xobnew (&pfile->buffer_ob, cpp_buffer);

1933  

1934     /* Clears, amongst other things, if_stack and mi_cmacro.  */

1935     memset (new, 0, sizeof (cpp_buffer));

1936  

1937     new->next_line = new->buf = buffer;

1938     new->rlimit = buffer + len;

1939     new->from_stage3 = from_stage3;

1940     new->prev = pfile->buffer;

1941     new->need_line = true;

1942  

1943     pfile->buffer = new;

1944     return new;

1945   }

 

缓存定义为以下的cpp_buffer。注意每个cpp_buffer对应一个宏定义。

 

268  struct cpp_buffer                                                                                in cpphash.h

269  {

270    const uchar *cur;              /* Current location.  */

271    const uchar *line_base;     /* Start of current physical line.  */

272    const uchar *next_line;     /* Start of to-be-cleaned logical line.  */

273 

274    const uchar *buf;              /* Entire character buffer.  */

275    const uchar *rlimit;          /* Writable byte at end of file.  */

276 

277    _cpp_line_note *notes;      /* Array of notes.  */

278    unsigned int cur_note;       /* Next note to process.  */

279    unsigned int notes_used;    /* Number of notes.  */

280    unsigned int notes_cap;     /* Size of allocated array.  */

281 

282    struct cpp_buffer *prev;

283 

284    /* Pointer into the file table; non-NULL if this is a file buffer.

285      Used for include_next and to record control macros.  */

286    struct _cpp_file *file;

287 

288    /* Value of if_stack at start of this file.

289      Used to prohibit unmatched #endif (etc) in an include file.  */

290    struct if_stack *if_stack;

291 

292   /* True if we need to get the next clean line.  */

293    bool need_line;

294 

295    /* True if we have already warned about C++ comments in this file.

296      The warning happens only for C89 extended mode with -pedantic on,

297      or for -Wtraditional, and only once per file (otherwise it would

298      be far too noisy).  */

299    unsigned char warned_cplusplus_comments;

300 

301    /* True if we don't process trigraphs and escaped newlines. True

302      for preprocessed input, command line directives, and _Pragma

303      buffers.  */

304    unsigned char from_stage3;

305 

306    /* At EOF, a buffer is automatically popped. If RETURN_AT_EOF is

307      true, a CPP_EOF token is then returned. Otherwise, the next

308      token from the enclosing buffer is returned.  */

309    unsigned int return_at_eof : 1;

310 

311     /* The directory of the this buffer's file. Its NAME member is not

312      allocated, so we don't need to worry about freeing it.  */

313    struct cpp_dir dir;

314 

315    /* Used for buffer overlays by cpptrad.c.  */

316    const uchar *saved_cur, *saved_rlimit;

317  };

4.1.3.1.2.1.4.2.        提取定义所在行号

注意在cpp_push_buffer中,我们要求缓存不能用于三元符(trigarphs)及转义换行符(//n)。因此在_cpp_clean_line中,从宏定义开头(deinifiton_lengthname_length)一直前进至行结束。

 

103    void

104    _cpp_clean_line (cpp_reader *pfile)                                                          in cpplex.c

105    {

106      cpp_buffer *buffer;

107      const uchar *s;

108      uchar c, *d, *p;

109   

110       buffer = pfile->buffer;

111       buffer->cur_note = buffer->notes_used = 0;

112       buffer->cur = buffer->line_base = buffer->next_line;

113       buffer->need_line = false;

114       s = buffer->next_line - 1;

115    

116       if (!buffer->from_stage3)

117       {

         

204      }

205      else

206      {

207        do

208          s++;

209        while (*s != '/n' && *s != '/r');

210        d = (uchar *) s;

211    

212        /* Handle DOS line endings.  */

213        if (*s == '/r' && s != buffer->rlimit && s[1] == '/n')

214          s++;

215      }

216   

217    done:

218      *d = '/n';

219      /* A sentinel note that should never be processed.  */

220      add_line_note (buffer, d + 1, '/n');

221      buffer->next_line = s + 1;

222    }

 

当我们从该函数返回,curnext_line指向定义的字面字符串。行的结尾被_cpp_line_note标记。

 

85      static void

86      add_line_note (cpp_buffer *buffer, const uchar *pos, unsigned int type)              in cpplex.c

87      {

88        if (buffer->notes_used == buffer->notes_cap)

89        {

90          buffer->notes_cap = buffer->notes_cap * 2 + 200;

91          buffer->notes = xrealloc (buffer->notes,

92                                buffer->notes_cap * sizeof (_cpp_line_note));

93        }

94     

95        buffer->notes[buffer->notes_used].pos = pos;

96        buffer->notes[buffer->notes_used].type = type;

97        buffer->notes_used++;

98      }

 

256    struct _cpp_line_note                                                                               in cpphash.h

257    {

258      /* Location in the clean line the note refers to.  */

259      const uchar *pos;

260   

261      /* Type of note. The 9 'from' trigraph characters represent those

262        trigraphs, '//' an escaped newline, ' ' an escaped newline with

263        intervening space, and anything else is invalid.  */

264      unsigned int type;

265    };

 

结构体cpp_macro是放置宏定义的地方。它具有如下定义。

 

88      struct cpp_macro                                                                                     in cpphash.h

89      {

90        /* Parameters, if any.  */

91        cpp_hashnode **params;

92     

93        /* Replacement tokens (ISO) or replacement text (traditional). See

94          comment at top of cpptrad.c for how traditional function-like

95          macros are encoded.  */

96        union

97        {

98          cpp_token *tokens;

99          const uchar *text;

100      } exp;

101   

102     /* Definition line number.  */

103      fileline line;

104   

105      /* Number of tokens in expansion, or bytes for traditional macros.  */

106      unsigned int count;

107   

108      /* Number of parameters.  */

109      unsigned short paramc;

110    

111      /* If a function-like macro.  */

112       unsigned int fun_like : 1;

113    

114       /* If a variadic macro.  */

115       unsigned int variadic : 1;

116    

117       /* If macro defined in system header.  */

118       unsigned int syshdr   : 1;

119    

120     /* Nonzero if it has been expanded or had its existence tested.  */

121      unsigned int used     : 1;

122    };

4.1.3.1.2.1.4.3.        传统模式

在深入宏定义的创建前,首先看一下宏的传统模式与标准模式间的差别。【6

传统(标准前)的C预处理器与标准预处理器有相当的不同。当GCC给定了-traditional选项,它尝试模拟传统预处理器。我们不保证GCC-traditional下的行为严格匹配任何标准化前的预处理器。

传统模式仅出于向后兼容的目的而存在。我们没有计划去扩展它,也不会改动它除非是修正严重的错误。你应该意识到现时的C库经常带有不符合传统模式的头文件。

下面是差异处的列表。它可能不完整,还可能不是正好对应于GCC或一个真实的传统预处理器的行为。

Ÿ        传统宏展开不理会单引号或双引号间的字符;宏参数符号被参数值所替代,即便它们出现在字符串或字符常量中。

Ÿ        传统地,允许在字符串或字符常量内完成宏展开。而该常量则继续展开入宏调用周围的文本(text)中。

Ÿ        然而,行的结尾会中止一个字符串或字符常量而不发生错误。(这是一个拼凑。传统模式通常用于预处理不是C的东西,它们有不同的注释语法。单个撇号通常出现在注释中。)

Ÿ        预处理指示只有当它们的前置#出现在第一列,才能被传统C识别。在行的开始和#之间不能有空格。

Ÿ        在传统C中,注释等同于空文本(在ISO C中,注释算做空格。)它可以像ISO C中的##那样使用,把宏参数粘在一起。

Ÿ        传统C没有预处理数字(preprocessing number)的概念。

Ÿ        在传统C中,宏在自己的定义中亦不被压制。因此,任何递归使用的宏不可避免会导致错误。

Ÿ        在传统C中没有###操作符。

Ÿ        在传统C中,宏展开后的文本可以和宏调用后的文本结合,产生单个符号(token),这在ISO C里是不可能的。

Ÿ        在传统模式下,GNU的预处理器扩展均不可用,除了部分实现的断言,而这些在将来可能会移除。

Ÿ        真实的传统C预处理器不能识别#elif#error,或#pragmaGCC即便在传统模式下也支持#elif#error,但不支持#pragma

Ÿ        传统模式是基于文本,而不是符号,注释是在宏展开后才去除的。因此,/**/可以用于把符号粘贴到一起,只要注释和粘贴符号之间没有空格。

Ÿ        传统模式保留了用户输入的空格的数目和形式。硬tab键仍然是硬tab键。如果你要预处理一个Makefile,这是有用的(不过我们不建议这样做)。

在传统C中,你可以通过-Wtraditional 选项,要求警告不存在,或功用有异的特性。这个选项仅当你不使用-traditional选项时才工作。当你使用一个符合ISO C的编译器用于传统C时,GCC不警告那些无法规避的ISO C特性,比如###操作符(注:这时注释不能粘贴符号)。

目前-Wtraditional给出如下警告:

Ÿ        在宏定义体中,宏参数出现在其字面字符串中。在传统C里,宏替换在字面字符串中执行,但ISO C不是这样。

Ÿ        在传统C中,某些预处理指示不存在。传统的预处理器仅当#出现在第一列时,才视该行为指示。因此-Wtraditional警告传统C能识别,但由于#不出现在第一列而被忽略的指示。这也暗示可以通过缩进,把传统C所不能识别的指示,如#pragma隐藏起来。某些传统的现实也不能识别#elif,因此最好不要使用它。

Ÿ        一个函数型的宏的不带参数列表的示现。在传统C中,这是个错误。在ISO C中,这仅表示宏不被展开。

Ÿ        一元加操作符。在传统C中不存在。

Ÿ        ULL整型常量后缀,在传统C中亦不存在。(对于简单长整型常量,传统C支持L后缀。)在系统头文件中定义的宏,使用这些后缀不会给出警告。例如,UINT_MAX可能被定义为4294967295U,但使用UINT_MAX 你不会得到警告。

通常你可以把常量写成不带U后缀的16进制,来避免这些警告,及相关的,关于过大的常量致使其无符号化的警告。注意,在奇异的情况下,这可能会导致错误的结果。