下面的flag_undef默认是0,但可由选项-undef改写。如果是非0值,则表示不要预定义任何系统特定或GCC特定的宏。但标准预定义的宏仍旧定义(即builtin_array所定义的宏)。
295 void
296 c_cpp_builtins (cpp_reader *pfile) in c-cppbuiltin.c
297 {
298 /* -undef turns off target-specific built-ins. */
299 if (flag_undef)
300 return;
301
302 define__GNUC__ ();
在GCC中,预定义的宏__GNUC__永远被定义为编译器的主版本号。例如,如果编译器的版本号是3.4.6,这个宏就被定义为3;而__GNUC_MINOR__,__GNUC_PATCHLEVEL__则分别被定义为4和6。另,宏__GNUG__类似于__GNC__,但用于C++。函数define__GNUC__根据保存了版本的字符串version_string,定义这些宏。
252 static void
253 define__GNUC__ (void) in c-cppbuiltin.c
254 {
255 /* The format of the version string, enforced below, is
256 ([^0-9]*-)?[0-9]+[.][0-9]+([.][0-9]+)?([- ].*)? */
257 const char *q, *v = version_string;
258
259 while (*v && ! ISDIGIT (*v))
260 v++;
261 if (!*v || (v > version_string && v[-1] != '-'))
262 abort ();
263
264 q = v;
265 while (ISDIGIT (*v))
266 v++;
267 builtin_define_with_value_n ("__GNUC__", q, v - q);
268 if (c_dialect_cxx ())
269 builtin_define_with_value_n ("__GNUG__", q, v - q);
270
271 if (*v != '.' || !ISDIGIT (v[1]))
272 abort ();
273 q = ++v;
274 while (ISDIGIT (*v))
275 v++;
276 builtin_define_with_value_n ("__GNUC_MINOR__", q, v - q);
277
278 if (*v == '.')
279 {
280 if (!ISDIGIT (v[1]))
281 abort ();
282 q = ++v;
283 while (ISDIGIT (*v))
284 v++;
285 builtin_define_with_value_n ("__GNUC_PATCHLEVEL__", q, v - q);
286 }
287 else
288 builtin_define_with_value_n ("__GNUC_PATCHLEVEL__", "0", 1);
289
290 if (*v && *v != ' ' && *v != '-')
291 abort ();
292 }
builtin_define_with_value_n将参数macro及expansion合并成一个定义对,并将其传给cpp_define。
492 static void
493 builtin_define_with_value_n (const char *macro, const char *expansion, size_t elen)
494 {
495 char *buf;
496 size_t mlen = strlen (macro);
497
498 /* Space for an = and a NUL. */
499 buf = alloca (mlen + elen + 2);
500 memcpy (buf, macro, mlen);
501 buf[mlen] = '=';
502 memcpy (buf + mlen + 1, expansion, elen);
503 buf[mlen + elen + 1] = '/0';
504
505 cpp_define (parse_in, buf);
506 }
cpp_define同时还处理由命令行定义的宏,命令行定义的宏具有形式m=val或m。对于前一个形式,它需要被转换成m val,而对于后一个,应该是m 1。 然后这个规范的形式可以被run_directive处理,就像我们前面所见。
1804 void
1805 cpp_define (cpp_reader *pfile, const char *str) in cpplib.c
1806 {
1807 char *buf, *p;
1808 size_t count;
1809
1810 /* Copy the entire option so we can modify it.
1811 Change the first "=" in the string to a space. If there is none,
1812 tack " 1" on the end. */
1813
1814 count = strlen (str);
1815 buf = alloca (count + 3);
1816 memcpy (buf, str, count);
1817
1818 p = strchr (str, '=');
1819 if (p)
1820 buf[p - str] = ' ';
1821 else
1822 {
1823 buf[count++] = ' ';
1824 buf[count++] = '1';
1825 }
1826 buf[count] = '/n';
1827
1828 run_directive (pfile, T_DEFINE, buf, count);
1829 }
C++在系统头文件stddef.h中也定义了一些预定义宏。它们是size_t,ptrdiff_t,wchar及 wint。c_stddef_cpp_builtins协助创建了与这些宏相关的对象。
c_cpp_builtins (continue)
304 /* For stddef.h. They require macros defined in c-common.c. */
305 c_stddef_cpp_builtins ();
下面函数调用中的第二个实参,如SIZE_TYPE等,定义都是目标平台相关的,并且它们都是表示类型名的字符串。
4249 void
4250 c_stddef_cpp_builtins(void) in c-common.c
4251 {
4252 builtin_define_with_value ("__SIZE_TYPE__", SIZE_TYPE, 0);
4253 builtin_define_with_value ("__PTRDIFF_TYPE__", PTRDIFF_TYPE, 0);
4254 builtin_define_with_value ("__WCHAR_TYPE__", MODIFIED_WCHAR_TYPE, 0);
4255 builtin_define_with_value ("__WINT_TYPE__", WINT_TYPE, 0);
4256 }
看到builtin_define_with_value把macro及expansion构成macro=expansion形式,不过前面我们已经看到在cpp_define中,该形式将进一步被转换成macro expansion,然后调用run_directive。
470 void
471 builtin_define_with_value (const char *macro, const char *expansion, int is_str)
472 {
473 char *buf;
474 size_t mlen = strlen (macro);
475 size_t elen = strlen (expansion);
476 size_t extra = 2; /* space for an = and a NUL */
477
478 if (is_str)
479 extra += 2; /* space for two quote marks */
480
481 buf = alloca (mlen + elen + extra);
482 if (is_str)
483 sprintf (buf, "%s=/"%s/"", macro, expansion);
484 else
485 sprintf (buf, "%s=%s", macro, expansion);
486
487 cpp_define (parse_in, buf);
488 }
下面的则是控制宏,它们描述了编译器及目标机器的特性。
编译器需要一些宏来描述语言的特性。在我们的程序里,我们可以使用这些宏来选择匹配的定义,及做出特别的处理。
c_cpp_builtins (continue)
307 if (c_dialect_cxx ())
308 {
309 if (SUPPORTS_ONE_ONLY)
310 cpp_define (pfile, "__GXX_WEAK__=1");
311 else
312 cpp_define (pfile, "__GXX_WEAK__=0");
313 if (warn_deprecated)
314 cpp_define (pfile, "__DEPRECATED");
315 }
316 /* Note that we define this for C as well, so that we know if
317 __attribute__((cleanup)) will interface with EH. */
318 if (flag_exceptions)
319 cpp_define (pfile, "__EXCEPTIONS");
320
321 /* Represents the C++ ABI version, always defined so it can be used while
322 preprocessing C and assembler. */
323 if (flag_abi_version == 0)
324 /* Use a very large value so that:
325
326 #if __GXX_ABI_VERSION >= <value for version X>
327
328 will work whether the user explicitly says "-fabi-version=x" or
329 "-fabi-version=0". Do not use INT_MAX because that will be
330 different from system to system. */
331 builtin_define_with_int_value ("__GXX_ABI_VERSION", 999999);
332 else if (flag_abi_version == 1)
333 /* Due to an historical accident, this version had the value
334 "102". */
335 builtin_define_with_int_value ("__GXX_ABI_VERSION", 102);
336 else
337 /* Newer versions have values 1002, 1003, .... */
338 builtin_define_with_int_value ("__GXX_ABI_VERSION",
339 1000 + flag_abi_version);
340
341 /* libgcc needs to know this. */
342 if (USING_SJLJ_EXCEPTIONS)
343 cpp_define (pfile, "__USING_SJLJ_EXCEPTIONS__");
基本类型特性可以由下面的宏获得。它们使得标准头文件给出的数值范围能正确地工作。而下面的TARGET_FLT_EVAL_METHOD是代表float.h头文件中FLT_EVAL_METHOD值的一个C表达式,FLT_EVAL_METHOD确定浮点表达式的评价方法,其值,
如果为-1,不定
如果为0,只是根据类型的范围及精度评估所有的操作数及常量。
如果为1,按double类型的范围及精度来评估类型为float及double的操作数及常量, 按long double类型的范围及精度来评估long double类型的操作数及常量。
如果为2,按long double类型的范围及精度来评估所有的操作数及常量。对于x86,该宏定义如下:
747 #define TARGET_FLT_EVAL_METHOD / in i386.h
748 (TARGET_MIX_SSE_I387 ? -1 : TARGET_SSE_MATH ? 0 : 2)
TARGET_MIX_SSE_I387在使用-mfpmath=sse,387时设置,表示同时表示387浮点协处理器,及SSE指令集中的纯量浮点指令(scalar floating point instructions),因而结果为不定。TARGET_SSE_MATH则是使用-mfpmath=sse时设置,表示使用SSE指令集中的纯量浮点指令。而默认的,x86使用387协处理器,其中间结果都具有80位的精度,而不是结果类型的精度。
c_cpp_builtins (continue)
345 /* limits.h needs to know these. */
346 builtin_define_type_max ("__SCHAR_MAX__", signed_char_type_node, 0);
347 builtin_define_type_max ("__SHRT_MAX__", short_integer_type_node, 0);
348 builtin_define_type_max ("__INT_MAX__", integer_type_node, 0);
349 builtin_define_type_max ("__LONG_MAX__", long_integer_type_node, 1);
350 builtin_define_type_max ("__LONG_LONG_MAX__", long_long_integer_type_node, 2);
351 builtin_define_type_max ("__WCHAR_MAX__", wchar_type_node, 0);
352
353 builtin_define_type_precision ("__CHAR_BIT__", char_type_node);
354
355 /* float.h needs to know these. */
356
357 builtin_define_with_int_value ("__FLT_EVAL_METHOD__",
358 TARGET_FLT_EVAL_METHOD);
359
360 builtin_define_float_constants ("FLT", "F", float_type_node);
361 builtin_define_float_constants ("DBL", "", double_type_node);
362 builtin_define_float_constants ("LDBL", "L", long_double_type_node);
363
364 /* For use in assembly language. */
365 builtin_define_with_value ("__REGISTER_PREFIX__", REGISTER_PREFIX, 0);
366 builtin_define_with_value ("__USER_LABEL_PREFIX__", user_label_prefix, 0);
367
368 /* Misc. */
369 builtin_define_with_value ("__VERSION__", version_string, 1);
370
371 /* Definitions for LP64 model. */
372 if (TYPE_PRECISION (long_integer_type_node) == 64
373 && POINTER_SIZE == 64
374 && TYPE_PRECISION (integer_type_node) == 32)
375 {
376 cpp_define (pfile, "_LP64");
377 cpp_define (pfile, "__LP64__");
378 }
在转换到实数模式一节中,可以看到GCC支持IEEE浮点标准。现在GCC把IEEE浮点标准的数据,由builtin_define_float_constants输出到预定义宏。这个函数比较简单,我们这里跳过。
在365行,REGISTER_PREFIX这个宏,展开为单个符号(不是字符串常量),这个符号用在目标平台的汇编语言中,作为寄存器名的前缀。你可以用它写出可在多个环境中适用的汇编。例如,在m68k-aout的环境中,它展开为空,但在m68k-coff的环境下,它展开为`%'。而__USER_LABEL_PREFIX__这个宏所展开的单个符号,在汇编中作为用户标签(C代码中可见的符号(symbol))的前缀。例如,在m68k-aout环境中,它被展开为`_',但在m68k-coff环境下,则展开为空。
这部分的宏向我们透露了有关编译器的行为。这些宏使得某些GNU头文件提供优化的定义,及选择使用系统库函数的宏或内联函数版本。
c_cpp_builtins (continue)
380 /* Other target-independent built-ins determined by command-line
381 options. */
382 if (optimize_size)
383 cpp_define (pfile, "__OPTIMIZE_SIZE__");
384 if (optimize)
385 cpp_define (pfile, "__OPTIMIZE__");
386
387 if (fast_math_flags_set_p ())
388 cpp_define (pfile, "__FAST_MATH__");
389 if (flag_really_no_inline)
390 cpp_define (pfile, "__NO_INLINE__");
391 if (flag_signaling_nans)
392 cpp_define (pfile, "__SUPPORT_SNAN__");
393 if (flag_finite_math_only)
394 cpp_define (pfile, "__FINITE_MATH_ONLY__=1");
395 else
396 cpp_define (pfile, "__FINITE_MATH_ONLY__=0");
397
398 if (flag_iso)
399 cpp_define (pfile, "__STRICT_ANSI__");
400
401 if (!flag_signed_char)
402 cpp_define (pfile, "__CHAR_UNSIGNED__");
403
404 if (c_dialect_cxx () && TREE_UNSIGNED (wchar_type_node))
405 cpp_define (pfile, "__WCHAR_UNSIGNED__");
406
407 /* Make the choice of ObjC runtime visible to source code. */
408 if (c_dialect_objc () && flag_next_runtime)
409 cpp_define (pfile, "__NEXT_RUNTIME__");
C预处理器通常预定义了一些表示所使用系统及机器的宏。对于每个由GCC支持的目标平台,它们显然是不一样的。所有特定于系统的预定义宏都展开为常量1,因此你可以使用#ifdef或#if来测试它们。
c_cpp_builtins (continue)
411 /* A straightforward target hook doesn't work, because of problems
412 linking that hook's body when part of non-C front ends. */
413 # define preprocessing_asm_p() (cpp_get_options (pfile)->lang == CLK_ASM)
414 # define preprocessing_trad_p() (cpp_get_options (pfile)->traditional)
415 # define builtin_define(TXT) cpp_define (pfile, TXT)
416 # define builtin_assert(TXT) cpp_assert (pfile, TXT)
417 TARGET_CPU_CPP_BUILTINS ();
418 TARGET_OS_CPP_BUILTINS ();
419 TARGET_OBJFMT_CPP_BUILTINS ();
420 }
第一组宏记录了CPU的信息。以x86为例,如果使用的目标机器是PentiumPro,那么TARGET_CPU_CPP_BUILTINS将定义以下宏。注意到builtin_define被定义为cpp_define。
548 #define TARGET_CPU_CPP_BUILTINS() / in i386.h
549 do /
550 { /
…
654 else if (ix86_arch == PROCESSOR_PENTIUMPRO) /
655 { /
656 builtin_define ("__i686"); /
657 builtin_define ("__i686__"); /
658 builtin_define ("__pentiumpro"); /
659 builtin_define ("__pentiumpro__"); /
660 } /
…
689 } /
690 while (0)
接下来是描述关于目标机器的语言特性的宏。以Linux OS作为例子。Linux支持PIC(位置无关代码),这个特性可以由GCC命令行选项打开或关闭,在该特性打开时定义__pic__及__PIC__(2者含义相同)。注意对于x86/Linux来说,flag_pic是1(“小”pic)或2(“大”pic)是没有区别的。
73 #define TARGET_OS_CPP_BUILTINS() / in linux.h
74 do /
75 { /
76 LINUX_TARGET_OS_CPP_BUILTINS(); /
77 if (flag_pic) /
78 { /
79 builtin_define ("__PIC__"); /
80 builtin_define ("__pic__"); /
81 } /
82 } /
83 while (0)
每个目标OS也要在相应的宏里铭刻上自己正式的名字。对于Linux,它定义了下面的宏。
100 #define LINUX_TARGET_OS_CPP_BUILTINS() / in linux.h
101 do { /
102 builtin_define ("__gnu_linux__"); /
103 builtin_define_std ("linux"); /
104 builtin_define_std ("unix"); /
105 builtin_assert ("system=linux"); /
106 builtin_assert ("system=unix"); /
107 builtin_assert ("system=posix"); /
108 } while (0)
函数builtin_define_std相当有趣,值得看一下。C/C++要求所有特定于系统的宏是“保留名字空间”(reserved namespace)的一部分。所有以“__”或“_”加上一个大写字母开头的名字,都是保留给编译器及库,随其所愿使用。不过,由于历史的原因,系统特定的宏有不带特别前缀的名字;例如,在Unix体系上,通常会定义unix。对于这样的宏,GCC提供了一个前后各添加了双下划线的平行宏(parallel macro)。那么,如果unix被定义,__unix__也将被定义。
431 void
432 builtin_define_std (const char *macro) in c-cppbuiltin.c
433 {
434 size_t len = strlen (macro);
435 char *buff = alloca (len + 5);
436 char *p = buff + 2;
437 char *q = p + len;
438
439 /* prepend __ (or maybe just _) if in user's namespace. */
440 memcpy (p, macro, len + 1);
441 if (!( *p == '_' && (p[1] == '_' || ISUPPER (p[1]))))
442 {
443 if (*p != '_')
444 *--p = '_';
445 if (p[1] != '_')
446 *--p = '_';
447 }
448 cpp_define (parse_in, p);
449
450 /* If it was in user's namespace... */
451 if (p != buff + 2)
452 {
453 /* Define the macro with leading and following __. */
454 if (q[-1] != '_')
455 *q++ = '_';
456 if (q[-2] != '_')
457 *q++ = '_';
458 *q = '/0';
459 cpp_define (parse_in, p);
460
461 /* Finally, define the original macro if permitted. */
462 if (!flag_iso)
463 cpp_define (parse_in, macro);
464 }
465 }
除了为目标OS定义宏外,我们还要为系统头文件中可能出现的断言准备答案。在[13]中,关于断言的细节给出如下:
"Assertions"是一个过时的,用于编写测试被编译程序将要运行的系统或机器的条件的,宏以外的方法。 断言通常是预定义的,但你可以通过预处理指示或命令行选项来定义它们。 断言意在提供一个更为系统的方法来描述编译器的目标系统。不过,在实践中,它们就像系统特定的预定义宏那样不可预测。另外,它们不是任何标准的组成部分,只有少数编译器支持它们。因而,使用断言的移植性比使用系统特定的预定义宏的要差。我们建议完全不要使用它们。 一个断言看上去就像这样: #PREDICATE (ANSWER) PREDICATE必须是一个标识符。ANSWER可以是任意的符号序列;除了开头及结尾的空白字符,所有的字符都是有意义的,并且其内部空白字符序列的差别将被忽略。(这与宏重定义的规则类似)因此,(x + y)与(x+y)不同,但与( x + y )相同。在一个答案中括号不能嵌套。 为了测试断言,你要把它写进一个#if。例如,如果vax或ns16000被断言为machine的答案,下面的条件成立。 #if #machine (vax) || #machine (ns16000) 你可以通过忽略条件中的答案,来测试一个述语(predicate)是否已断言了任何答案: #if #machine 断言由#assert指示生成。它的唯一的参数是要生成的断言,它就是条件中的断言部分,但没有开头的#。 #assert PREDICATE (ANSWER) 你也可以为同一个述语给不同的答案以产生多个断言。同一述语后面的断言不会覆盖前面的断言。任一述语的所有答案都是同时为真(true)。 断言可以通过#unassert指示取消。它的句法与#assert相同。在这个形式中,它只取消了在#unassert所在行上指明的答案,这个述语的其他答案仍然为真。你可以不指明答案来取消整个述语: #unassert PREDICATE 在这2个形式中,如果没有生成过这样的断言,#unassert没有效果。 |
1854 void
1855 cpp_assert (cpp_reader *pfile, const char *str) in cpplib.c
1856 {
1857 handle_assertion (pfile, str, T_ASSERT);
1858 }
在[10]中,记录的用法是#assert PRED=ANS。因而handle_assertion将其转之为形式#assert PRED(ANS)。
1868 static void
1869 handle_assertion (cpp_reader *pfile, const char *str, int type) in cpplib.c
1870 {
1871 size_t count = strlen (str);
1872 const char *p = strchr (str, '=');
1873
1874 /* Copy the entire option so we can modify it. Change the first
1875 "=" in the string to a '(', and tack a ')' on the end. */
1876 char *buf = alloca (count + 2);
1877
1878 memcpy (buf, str, count);
1879 if (p)
1880 {
1881 buf[p - str] = '(';
1882 buf[count++] = ')';
1883 }
1884 buf[count] = '/n';
1885 str = buf;
1886
1887 run_directive (pfile, type, str, count);
1888 }
对于断言,其处理句柄是do_assert,它由run_directive来调用。
1733 static void
1734 do_assert (cpp_reader *pfile) in cpplib.c
1735 {
1736 struct answer *new_answer;
1737 cpp_hashnode *node;
1738
1739 node = parse_assertion (pfile, &new_answer, T_ASSERT);
1740 if (node)
1741 {
1742 /* Place the new answer in the answer list. First check there
1743 is not a duplicate. */
1744 new_answer->next = 0;
1745 if (node->type == NT_ASSERTION)
1746 {
1747 if (*find_answer (node, new_answer))
1748 {
1749 cpp_error (pfile, CPP_DL_WARNING, "/"%s/" re-asserted",
1750 NODE_NAME (node) + 1);
1751 return;
1752 }
1753 new_answer->next = node->value.answers;
1754 }
1755
1756 node->type = NT_ASSERTION;
1757 node->value.answers = new_answer;
1758 BUFF_FRONT (pfile->a_buff) += (sizeof (struct answer)
1759 + (new_answer->count - 1)
1760 * sizeof (cpp_token));
1761 check_eol (pfile);
1762 }
1763 }
作为cpp_hashnode的一部分,answer具有以下的定义。下面的域first被设计为可变长度的数值,用于记录构成答案的符号。
29 struct answer in cpplib.c
30 {
31 struct answer *next;
32 unsigned int count;
33 cpp_token first[1];
34 };
注意到在答案中的符号不能像宏那样可以展开。因为它的行为异于宏,又是由预处理器处理,系统需要一个方法来区分它们与宏。因此GCC在断言的名字前加上‘#’。
1651 static cpp_hashnode *
1652 parse_assertion (cpp_reader *pfile, struct answer **answerp, int type) in cpplib.c
1653 {
1654 cpp_hashnode *result = 0;
1655 const cpp_token *predicate;
1656
1657 /* We don't expand predicates or answers. */
1658 pfile->state.prevent_expansion++;
1659
1660 *answerp = 0;
1661 predicate = cpp_get_token (pfile);
1662 if (predicate->type == CPP_EOF)
1663 cpp_error (pfile, CPP_DL_ERROR, "assertion without predicate");
1664 else if (predicate->type != CPP_NAME)
1665 cpp_error (pfile, CPP_DL_ERROR, "predicate must be an identifier");
1666 else if (parse_answer (pfile, answerp, type) == 0)
1667 {
1668 unsigned int len = NODE_LEN (predicate->val.node);
1669 unsigned char *sym = alloca (len + 1);
1670
1671 /* Prefix '#' to get it out of macro namespace. */
1672 sym[0] = '#';
1673 memcpy (sym + 1, NODE_NAME (predicate->val.node), len);
1674 result = cpp_lookup (pfile, sym, len + 1);
1675 }
1676
1677 pfile->state.prevent_expansion--;
1678 return result;
1679 }
答案是由parse_answer提前的。这个函数很简单,与提前宏定义的函数很相似。
1575 static int
1576 parse_answer (cpp_reader *pfile, struct answer **answerp, int type) in cpplib.c
1577 {
1578 const cpp_token *paren;
1579 struct answer *answer;
1580 unsigned int acount;
1581
1582 /* In a conditional, it is legal to not have an open paren. We
1583 should save the following token in this case. */
1584 paren = cpp_get_token (pfile);
1585
1586 /* If not a paren, see if we're OK. */
1587 if (paren->type != CPP_OPEN_PAREN)
1588 {
1589 /* In a conditional no answer is a test for any answer. It
1590 could be followed by any token. */
1591 if (type == T_IF)
1592 {
1593 _cpp_backup_tokens (pfile, 1);
1594 return 0;
1595 }
1596
1597 /* #unassert with no answer is valid - it removes all answers. */
1598 if (type == T_UNASSERT && paren->type == CPP_EOF)
1599 return 0;
1600
1601 cpp_error (pfile, CPP_DL_ERROR, "missing '(' after predicate");
1602 return 1;
1603 }
1604
1605 for (acount = 0;; acount++)
1606 {
1607 size_t room_needed;
1608 const cpp_token *token = cpp_get_token (pfile);
1609 cpp_token *dest;
1610
1611 if (token->type == CPP_CLOSE_PAREN)
1612 break;
1613
1614 if (token->type == CPP_EOF)
1615 {
1616 cpp_error (pfile, CPP_DL_ERROR, "missing ')' to complete answer");
1617 return 1;
1618 }
1619
1620 /* struct answer includes the space for one token. */
1621 room_needed = (sizeof (struct answer) + acount * sizeof (cpp_token));
1622
1623 if (BUFF_ROOM (pfile->a_buff) < room_needed)
1624 _cpp_extend_buff (pfile, &pfile->a_buff, sizeof (struct answer));
1625
1626 dest = &((struct answer *) BUFF_FRONT (pfile->a_buff))->first[acount];
1627 *dest = *token;
1628
1629 /* Drop whitespace at start, for answer equivalence purposes. */
1630 if (acount == 0)
1631 dest->flags &= ~PREV_WHITE;
1632 }
1633
1634 if (acount == 0)
1635 {
1636 cpp_error (pfile, CPP_DL_ERROR, "predicate's answer is empty");
1637 return 1;
1638 }
1639
1640 answer = (struct answer *) BUFF_FRONT (pfile->a_buff);
1641 answer->count = acount;
1642 answer->next = NULL;
1643 *answerp = answer;
1644
1645 return 0;
1646 }
find_answer设计来查找candidate给定的答案,是否已经加入node中。注意到答案包含了符号,其相等性应由_cpp_equiv_tokens一个一个符号来检查确定。同一答案不能被重新插入。
1683 static struct answer **
1684 find_answer (cpp_hashnode *node, const struct answer *candidate) in cpplib.c
1685 {
1686 unsigned int i;
1687 struct answer **result;
1688
1689 for (result = &node->value.answers; *result; result = &(*result)->next)
1690 {
1691 struct answer *answer = *result;
1692
1693 if (answer->count == candidate->count)
1694 {
1695 for (i = 0; i < answer->count; i++)
1696 if (! _cpp_equiv_tokens (&answer->first[i], &candidate->first[i]))
1697 break;
1698
1699 if (i == answer->count)
1700 break;
1701 }
1702 }
1703
1704 return result;
1705}
最后TARGET_OBJFMT_CPP_BUILTINS创建了关于目标格式的宏定义。对于x86/Linux,其默认的目标格式是elf。
24 #define TARGET_OBJFMT_CPP_BUILTINS() / in elfso.h
25 do /
26 { /
27 builtin_define ("__ELF__"); /
28 } /
29 while (0)