这是我刚刚在工作中遇到的一些代码的简化版本:
#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行的函数。
预处理器如何解析这样的代码?
答案 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)
。