您的位置:首页 > 编程语言

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

2010-04-23 09:27 603 查看
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_length – name_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 }

当我们从该函数返回,cur及next_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,或#pragma。GCC即便在传统模式下也支持#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中不存在。
Ÿ U及LL整型常量后缀,在传统C中亦不存在。(对于简单长整型常量,传统C支持L后缀。)在系统头文件中定义的宏,使用这些后缀不会给出警告。例如,UINT_MAX可能被定义为4294967295U,但使用UINT_MAX 你不会得到警告。
通常你可以把常量写成不带U后缀的16进制,来避免这些警告,及相关的,关于过大的常量致使其无符号化的警告。注意,在奇异的情况下,这可能会导致错误的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: