gcc预处理器与MS VS cl之间的另一个区别。请考虑以下代码段:
# define A(x) L ## x
# define B A("b")
# define C(x) x
C(A("a" B))
对于'gcc -E',我们得到以下内容:
L"a" A("b")
对于'cl / E',输出不同:
L"a" L"b"
MS预处理器以某种方式执行额外的宏扩展。它的工作算法明显不同于gcc,但这个算法似乎也是一个秘密。有谁知道如何解释观察到的差异以及MS cl中的预处理方案是什么?
答案 0 :(得分:4)
海湾合作委员会是正确的。该标准规定:
C99 6.10.3.4/2(以及C ++ 98/11 11.3.4 / 2):如果在替换清单扫描期间找到要替换的宏的名称 (不包括源文件的其余预处理标记),它不会被替换。
因此,在展开A("a" B)
时,我们首先将B
替换为A("a" A("B"))
。
A("B")
未已被替换,因此最终结果为L"a" A("B")
。
答案 1 :(得分:3)
迈克的回答是正确的,但他实际上忽略了标准的关键部分,说明了为什么会这样:
6.10.3.4/2 如果在此替换清单扫描期间找到要替换的宏的名称 (不包括源文件的其余预处理标记),它不会被替换。 此外,如果任何嵌套替换遇到要替换的宏的名称, 它没有被替换。这些未替换的宏名称预处理标记不再存在 可用于进一步更换即使它们稍后(重新)检查在其中 否则宏名称预处理令牌将被替换。
请注意我强调的最后一句。
因此gcc和MSVC都将宏A("a" B)
扩展为L"a" A("b")
,但有趣的情况(MSVC搞砸了)是宏被C
宏包裹的时候。
扩展C
宏时,首先会检查其参数是否要扩展宏,并展开A
。然后将其替换为C的主体,然后再扫描该主体以寻找要替换的宏。现在您可能会认为,由于这是C
的扩展,因此只会跳过名称C
,但最后一句意味着来自A
扩展的标记也会跳过重新扩展A。
答案 2 :(得分:0)
基本上有两种方法可以让人们认为A宏的剩余出现应该被替换:
第一个是插入之前的处理或宏参数,代替宏的替换列表中的相应参数。通常每个参数都是完全宏替换的,就像它形成了输入文件的其余部分一样,如6.10.3.1节所述 标准。但是,如果参数(此处为:x)出现在##旁边,则不;在这种情况下,参数只是根据6.10.3.3的参数替换,没有任何递归的宏替换。
第二种方式是第6.10.3.4节的“重新扫描和进一步替换”,但对于已经被替换过一次的宏,这不是递归的。
因此在这种情况下都不适用,这意味着gcc在保留A未替换的A的位置时是正确的。