gcc&MS和MS预处理器之间的另一个区别

时间:2012-07-13 11:43:27

标签: visual-studio gcc c-preprocessor

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中的预处理方案是什么?

3 个答案:

答案 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的位置时是正确的。