为什么这个C或C ++宏没有被预处理器扩展?

时间:2010-06-09 06:27:02

标签: c++ c macros c-preprocessor

当使用gcc 4.1.0编译时,有人能指出代码中的问题。

#define X 10
int main()
{
  double a = 1e-X;
  return 0;
}

我收到错误:指数没有数字。

当我用10替换X时,它工作正常。我还用g ++ -E命令检查了应用了预处理器的文件,它没有用10替换X. 我的印象是预处理器用替换文本替换文件中定义的每个宏并应用任何智能。我错了吗?

我知道这是一个非常愚蠢的问题,但我很困惑,我宁愿愚蠢而不是混淆:)。

有任何意见/建议吗?

4 个答案:

答案 0 :(得分:17)

预处理器不是文本处理器,它在令牌级别上工作。在您的代码中,在定义之后,令牌X的每个出现都将被令牌10替换。但是,代码的其余部分中没有令牌X

1e-X在语法上是无效的,无法转换为令牌,这基本上就是错误告诉你的内容(它表示要使它成为有效的令牌 - 在这种情况下是一个浮点文字 - 你必须提供有效的指数)。

答案 1 :(得分:14)

当你像这样写1e-X时,X不是预处理器要替换的单独符号 - 任何一方都需要有空格(或某些其他符号)。想一想,你会明白为什么...... :)

编辑:“12-X”有效,因为它被解析为“12”,“ - ”,“X”这三个独立的令牌。 “1e-X”不能像那样分裂,因为“1e-”本身并不构成有效的标记,正如Jonathan在他的回答中提到的那样。

至于问题的解决方案,您可以使用令牌连接:

#define E(X) 1e-##X
int main()
{
  double a = E(10); // expands to 1e-10
  return 0;
}

答案 2 :(得分:9)

有几个人说1e-X被作为一个单一的标记,这是部分正确的。解释:

翻译期间有两类令牌:预处理令牌令牌。源文件最初被分解为预处理令牌;然后,这些令牌将用于所有预处理任务,包括宏替换。在预处理之后,每个预处理令牌都被转换为令牌;这些产生的标记在实际编译过程中使用。

预处理令牌的类型少于令牌类型。例如,关键字(例如forwhileif)在预处理阶段并不重要,因此没有关键字预处理令牌。关键字简单地作为标识符。当从预处理标记到标记的转换发生时,检查每个标识符预处理标记;如果它与关键字匹配,则将其转换为关键字标记;否则它将被转换为标识符令牌。

预处理期间只有一种类型的数字标记:预处理编号。这种类型的预处理令牌对应于两种不同类型的令牌:整数字面浮动字面

预处理号预处理令牌的定义非常广泛。实际上,它匹配以数字或小数点开头的任何字符序列,后跟任意数量的数字,非数字(例如字母),以及e+e-。因此,以下所有都是有效的预处理数预处理标记:

1.0e-10
.78
42
1e-X
1helloworld

前两个可以转换为浮动文字;第三个可以转换为整数文字。最后两个不是有效的整数文字或浮动文字;那些预处理令牌无法转换为令牌。这就是为什么你可以无错误地预处理源但无法编译它的原因:从预处理标记到标记的转换中出现错误。

答案 3 :(得分:6)

GCC 4.5.0也不会改变X.

答案将取决于预处理器如何解释预处理令牌 - 以及“最大蒙克”规则。 “最大蒙克”规则规定“x +++++ y”被视为“x ++ ++ + y”因此是错误的,而不是'x ++ + ++ y',这是合法。

问题是为什么预处理器将'1e-X'解释为单个预处理令牌。显然,它会将'1e-10'视为单一令牌。对于'1e-'没有有效的解释,除非它经过预处理器后跟一个数字。 所以,我必须猜测预处理器将'1e-X'视为单个令牌(实际上是错误的)。但是我没有在标准中解析正确的条款以确定它的位置。但是标准中的“预处理数”或“pp-number”的定义(见下文)与有效整数或浮点常数的定义略有不同,并允许许多无效的“pp-number”作为整数或浮点常数。

如果有帮助,“cc -E -v soq.c”的Sun C编译器的输出是:

# 1 "soq.c"
# 2
int main()
{
"soq.c", line 4: invalid input token: 1e-X
  double a =  1e-X ;
  return 0;
}
#ident "acomp: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25"
cc: acomp failed for soq.c

所以,至少有一个C编译器拒绝预处理器中的代码 - 可能是GCC预处理器有点松懈(我试图用gcc -Wall -pedantic -std=c89 -Wextra -E soq.c来引发它,但它没有发出吱吱声) 。在宏和'1e-XXX'符号中使用3个X表示所有三个X都被GCC和Sun C编译器消耗。

C预处理编号的标准定义

来自C标准 - ISO / IEC 9899:1999§6.4.8预处理编号:

pp-number:
    digit
    . digit
    pp-number digit
    pp-number identifier-nondigit
    pp-number e sign
    pp-number E sign
    pp-number p sign
    pp-number P sign
    pp-number .

鉴于此,'1e-X'是有效的'pp-number',因此X不是单独的标记('1e-XXX'中的'XXX'也不是单独的标记)。因此,预处理器无法扩展X;它不是一个可以扩展的单独标记。