【问题标题】:Unexpected Behaviour of GCC and VC++ PreprocessorGCC 和 VC++ 预处理器的意外行为
【发布时间】:2014-04-02 09:21:26
【问题描述】:

我正在尝试了解 C++ 标准预处理器要求。我创建的一个有点棘手的示例在 GCC 和 VC++2010 中产生了令人惊讶的结果:

#define a(x,y) x##y
#define tzsW kka
a(t,zs  )W

GCC 产量:

tzs W

注意在 W 之前添加的额外空格。

VC++2010 产出:

tzsW

请注意,在 W 之前没有添加空格,但标识符没有进一步扩展。我扫描了 C++03 标准,找不到任何东西说我们应该阻止像在 gcc 中一样创建新标识符 (tzsW)。并且没有什么可以阻止这个新标识符进一步扩展宏(VC++ 行为)。

为什么 GCC 和 VC++2010 不喜欢新标识符?

编辑

如果使用了另一个宏调用,例如

a(t,zs  )[]

gcc 产量:

tzs[]

注意没有加空格,显示gcc故意给我之前的case加空格。

【问题讨论】:

    标签: c++ gcc visual-c++ c-preprocessor


    【解决方案1】:

    预处理器的输出是标记,而不是纯文本。除非您使用标记粘贴运算符,否则标记通常不会在预处理中组合。

    当您查看预处理步骤的输出时,您必须将标记转换为文本。 gcc 会插入空格,这样您就不会误以为 tzsW 是单个标记。在tzs[ 的情况下不需要这样做,因为[ 不是有效的标识符字符,因此不会造成混淆。

    两个编译器都没有将tzsW 视为要重新扩展的单个标记是正确的。

    请注意,Visual C++ documentation 承认,与编译原始源代码相比,编译预处理器的输出可能会产生不同且不正确的输出,因为它们在将预处理输出转换为文本时不会插入空格来分隔标记.当预处理器输出在正常操作中直接传递到编译器的下一个阶段时,不会发生这种情况。

    【讨论】:

    • 我认为 VS2010 将 tzsW 组合成一个标识符可能是错误的,而 GCC 将它们分开是正确的。
    • @JonathanLeffler:但它并没有将它们组合成一个标识符,正如tzsW 没有被替换的面部所指示的那样。如果您从编译中间抽取输出,只是从标记到文本的有损翻译使它们看起来像一个单一的标识符。
    • 好的;它在产生人类可以理解的输出方面很草率。
    【解决方案2】:
    1. 空格还是没有空格?

      我没有在标准中找到参考,但this article 讨论了这个问题。简而言之,决定是否要那个空间并不容易......

    2. 是否将tzsW 替换为kka

      参见标准中的 16.3.4:

      替换列表中的所有参数都被替换后, 生成的预处理令牌序列被重新扫描 源文件的后续预处理标记以获得更多宏 要替换的名称。

      这表明这里VC的行为不正确。

    【讨论】:

    • 标准规定tzsW要换成kka,VC不这样做。
    • 嗯,只有当它是一个单一的令牌。在示例中,这不是因为tzs 来自a(t,zs ) 的扩展,而W 只是W。两种编译器的行为都是一样的(它们不会产生kka,只是产生tzsW)所以我不明白你怎么能把一个不正确而不是另一个。
    • 这有点模棱两可。 GCC 不会产生tzsW,所以它当然不会考虑替换它。例如,如果你写a(tzs,W),GCC 将产生kka。那么,您是说a(tzs,W)a(tz,s)W 是不同的东西(对于VC,事件都导致tzsW)并且VC 会用kka 代替第一个,而不是第二个?
    • 是的,a(tz,s)Wa(tzs,W) 扩展为不同的标记序列,并且它们在两个平台上都扩展为相同的标记序列。它只是将标记序列写回引入明显歧义的文本。
    猜你喜欢
    • 2014-09-13
    • 2011-06-09
    • 2011-06-16
    • 2014-06-14
    • 2011-02-06
    • 1970-01-01
    • 2012-07-13
    • 2011-04-24
    • 1970-01-01
    相关资源
    最近更新 更多