似乎预处理器在连接有符号数字的令牌时添加了一个空格。我试过这个:
#define DECL_FL(IE) 1e##IE##f
float val[] =
{
DECL_FL(12),
DECL_FL(-12),
DECL_FL(+12),
};
我运行预处理器:
$ gcc test.c -E
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
float val[] =
{
1e12f,
1e- 12f,
1e+ 12f,
};
数组中的第一个元素是正确的。对于其他两个元素,在数字和符号之间添加一个空格。
预处理器为什么添加空格?我该如何避免?
简短的版本是+12
不是预处理器令牌,-12
也不是;相反,它们中的每一个都是两个预处理器标记(即,+
和-
是标点符号;12
分别是pp号(。
我们本质上是在处理一个pp号码,所以这里是pp号码的语法规则:
pp-number:
digit
.digit
pp-numberdigit
pp-numberidentifier-nondigit
pp-numberesign
pp-numberEsign
pp-numberpsign
pp-numberPsign
pp-number.
注意,1
、1e+
、1e+12
和1e+12f
都是pp编号,但+12
和-12
不是。这就是咬你的东西。
根据预处理器规则,替换列表中的参数被参数令牌序列替换;则应用每个##
。应用程序删除##
,并将前一个令牌连接到下一个令牌。如果该组合不是有效的预处理器令牌,则结果是未定义的。(作为参考,预处理器标记可以是标头名称、标识符、pp编号、字符常量、字符串文字、标点符号,也可以是非空白字符,仅此而已(。
当在IE为+12的情况下应用1e##IE##f时,您基本上是在做<CCD_ 15>###<+
><CCD_ 17>###<f
>,使用大括号表示各个标记。无论顺序如何(这很好,因为##顺序未指定(,两种粘贴都将产生有效的pp编号<CCD_ 19>和<12f
>。但结果会给你留下<CCD_ 21><12f
>而不是期望的<1e+12f
>。
如何避免
不幸的是,您不得不放弃传递+12
和-12
令牌序列作为参数。您可以接受+, 12
作为两个参数,但在步骤中组合这些参数时需要小心,因为##
运算符的顺序未指定,并且+12
不是有效的预处理器令牌(否则它可能会工作,但不允许…并且的情况可能是一颗噩梦般的定时炸弹(:
#define PASTE(A,B) A##B
#define DECL_FL(IE) 1e##IE##f
#define DECL_FL_SGN(S,IE) PASTE(1e##S,IE##f)
float val[] =
{
DECL_FL(12),
DECL_FL_SGN(+,12),
DECL_FL_SGN(-,12),
};
或者您可以简单地使用特定于符号的宏;严格地说,由于1e12f
和1e+12f
是相同的值(除非他们只是运行预处理器,否则没有人会看到它(,你只能使用两个宏:
#define DECL_FL(IE) 1e##IE##f
#define DECL_FL_E_NEG(IE) 1e-##IE##f
float val[] =
{
DECL_FL(12),
DECL_FL_E_NEG(12),
DECL_FL(12),
};
##
是标记粘贴运算符,如果使用后没有形成有效的标记,这将不起作用,因为1e+12f
不是有效的预处理器标记,所以它试图插入空间,否则可能导致意外行为,您可以在以下文档中找到它。
http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html