C预处理器将标识符视为对象而不是类似函数

时间:2011-11-16 00:12:51

标签: c macros c-preprocessor

这是我刚刚在工作中遇到的一些代码的简化版本:

#include <stdio.h>

#define F(G) G(1)
#define G(x) x+1

int main() {
  printf("%d\n", F(G));
}

打印2。

现在,我可以看到F(G)扩展为G(1)然后G(1)扩展为2,但我不明白为什么。我本来期望得到一个错误,G不是printf行的函数。

预处理器如何解析这样的代码?

4 个答案:

答案 0 :(得分:6)

类似函数的宏只有在其名称后跟(时才会被调用。

F(G)中,G后面没有(,因此G没有宏调用。

F(G) G(1)中,G是一个宏参数,因此不会直接进行宏替换(这是一个非常令人困惑的宏:-O)。在G(1)中,G被对应于参数G的参数替换,该参数也恰好是G。然后重新扫描,然后将G(1)评估为1 + 1

如果我们重写您的宏以便您不会以多种不同的方式使用G,那么它就更容易理解:

#define F(x) x(1)
#define G(x) x + 1

此处,F(G)G(1)取代。然后重新扫描,然后评估G的调用,产生1 + 1

答案 1 :(得分:1)

扩展James McNellis的答案,C99标准规定:

  

6.10.3.4重新扫描和进一步更换

     

1替换列表中的所有参数已被替换并且#和##   处理已经发生,所有地标预处理令牌都被删除。然后,   重新扫描生成的预处理标记序列以及所有后续标记序列   预处理源文件的标记,以便更换更多的宏名称。

     

2如果在此替换列表扫描期间找到要替换的宏的名称   (不包括源文件的其余预处理令牌),它不会被替换。   此外,如果任何嵌套替换遇到要替换的宏的名称,   它没有被替换。这些未替换的宏名称预处理标记不再存在   可用于进一步更换,即使它们稍后(重新)检查在其中   否则,宏名称预处理令牌将被替换。

     

3不处理由此产生的完全宏替换的预处理标记序列   作为预处理指令,即使它类似于一个,但是所有的pragma一元运算符   然后在其中的表达式按照下面6.10.9中的规定进行处理。

答案 2 :(得分:0)

#defines做非常基本的字符串替换:

printf(“%d \ n”,F(G));

转到

printf(“%d \ n”,G(1));

转到:

printf(“%d \ n”,1 + 1);

答案 3 :(得分:0)

预处理器进行一次传递,但您认为每次#define传递一次。因此,在传递期间,预处理器会匹配并替换F(G)但不匹配任何G(x)