c预处理器在用符号连接数字时添加一个空格



似乎预处理器在连接有符号数字的令牌时添加了一个空格。我试过这个:

#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. 注意,11e+1e+121e+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),
};

或者您可以简单地使用特定于符号的宏;严格地说,由于1e12f1e+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