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

Atu*_*ul 31 c c++ macros c-preprocessor

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

#define X 10
int main()
{
  double a = 1e-X;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

有什么意见/建议吗?

ava*_*kar 17

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

1e-X 在语法上是无效的,不能变成一个令牌,这基本上是错误告诉你的(它说使它成为一个有效的令牌 - 在这种情况下是一个浮点文字 - 你必须提供一个有效的指数).


tza*_*man 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这并没有真正解释为什么在这种情况下`-`不被认为是"某些其他符号"之一,特别是当`int y = 12-X;`工作正常时. (2认同)

Jam*_*lis 9

有几个人说这1e-X是一个单一的令牌,这是部分正确的.解释:

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

预处理令牌的类型少于令牌类型.例如,关键字(例如for,while,if)不是在预处理阶段显著,所以没有关键字预处理标记.关键字简单地作为标识符.当从预处理标记到标记的转换发生时,检查每个标识符预处理标记; 如果它与关键字匹配,则将其转换为关键字标记; 否则将其转换为标识符令牌.

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

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

1.0e-10
.78
42
1e-X
1helloworld
Run Code Online (Sandbox Code Playgroud)

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


Jon*_*ler 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
Run Code Online (Sandbox Code Playgroud)

因此,至少有一个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 .
Run Code Online (Sandbox Code Playgroud)

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