17

我发现 Microsoft Visual Studio 编译器和 gcc 以不同的方式预处理以下小片段:

# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)

'gcc -E' 给出以下内容:

a + {a + b}

, 而 'cl /E' 发出有关缺少宏参数的警告并产生以下输出:

a + {a, b} +

似乎来自嵌套宏扩展的逗号不被视为参数分隔符。不幸的是,我没有找到在 cl 预处理器中实现的算法的描述,所以我不确定我的建议是否正确。有谁知道 cl 预处理器是如何工作的,它的算法和 gcc 有什么区别?以及如何解释观察到的行为?

4

3 回答 3

9
# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)

让我们一步一步地手动推出:

M(a, b)
--> M2(a, P(a, b))
--> M2(a, {a, b})

标准说:

列表中的各个参数由逗号预处理标记分​​隔,但匹配内括号之间的逗号预处理标记不分隔

只提到了括号,所以...

--> M3(a, {a, b})
--> a + {a + b}

重要的:

M3(a, {a, b})

这里,根据标准的前面引用,将三个“参数”传递给 M3(使用单引号来描述标记/参数):

M3('a', '{a', 'b}')

扩展到

'a' + '{a' + 'b}'

这就是cpp(4.6.1)逐字给出的:

# 1 "cpp.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "cpp.cpp"




a + {a + b}

cpp(或gccg++)是正确的,MSVC 不是。

作为贵族,请确保存在错误报告。

于 2012-07-13T11:52:46.220 回答
4

解释这种行为的唯一逻辑如下所示。

CL方式:

 M(a,b) 
 M2(a,P(a,b)) 
 M3(a,P(a,b))
 M3(a,{a,b}) -> M3 gets 2 arguments ( 'a' and '{a,b}') instead of 3.
    |  \ /
  arg1  |
      arg2 

gcc方式:

M(a,b) 
M2(a,P(a,b)) 
M3(a,P(a,b))
M3(a,{a,b}) -> Gcc probably thinks there are 3 arguments here ('a', '{a', 'b}').
   |  | |
 arg1 | |
   arg2 |
     arg3
于 2012-07-13T11:41:19.310 回答
1

我认为 gcc 是正确的,微软的做法是不正确的。

对行进行宏替换时

M2(a, P(a, b))

标准(第 6.10.3.1 节)要求在用其参数(“P(a,b)”)替换宏的替换列表(“M3(x,y)”)中的第二个参数(“y”)之前,宏将对该参数执行替换。这意味着“P(a, b)”在插入之前被处理为“{a, b}”,导致

M3(a, {a, b})

然后进一步替换为

a + {a + b}
于 2012-07-13T11:52:04.467 回答