C 预处理器,递归宏 [英] C preprocessor, recursive macros
问题描述
为什么 M(0) 和 N(0) 的结果不同?
Why does M(0) and N(0) have different results?
#define CAT_I(a, b) a ## b
#define CAT(a, b) CAT_I(a, b)
#define M_0 CAT(x, y)
#define M_1 whatever_else
#define M(a) CAT(M_, a)
M(0); // expands to CAT(x, y)
#define N_0() CAT(x, y)
#define N_1() whatever_else
#define N(a) CAT(N_, a)()
N(0); // expands to xy
推荐答案
其实这要看你对语言标准的理解了.例如,在 mcpp 下,一个严格符合语言标准文本的预处理器实现,第二个也产生 CAT(x, y);
[额外的换行符已从结果中删除]:
In fact, it depends on your interpretation of the language standard. For example, under mcpp, a preprocessor implementation that strictly conforms to the text of the language standard, the second yields CAT(x, y);
as well [extra newlines have been removed from the result]:
C:dev>mcpp -W0 stubby.cpp
#line 1 "C:/dev/stubby.cpp"
CAT(x, y) ;
CAT(x, y) ;
C:dev>
一个已知的不一致C++ 语言规范(C 规范中存在相同的不一致,尽管我不知道 C 的缺陷列表在哪里).规范声明最终的
There is a known inconsistency in the C++ language specification (the same inconsistency is present in the C specification, though I don't know where the defect list is for C). The specification states that the final CAT(x, y)
should not be macro-replaced. The intent may have been that it should be macro-replaced.
引用链接的缺陷报告:
早在 1980 年代,几位 WG14 人员就知道,不可替代"和不可替代"之间存在微小差异.废话和产生伪代码的尝试.
Back in the 1980's it was understood by several WG14 people that there were tiny differences between the "non-replacement" verbiage and the attempts to produce pseudo-code.
委员会的决定是在野外"没有现实的计划.会冒险进入这个领域,并且试图减少不确定性是不值得冒改变实现或程序的一致性状态的风险.
The committee's decision was that no realistic programs "in the wild" would venture into this area, and trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs.
那么,为什么 M(0)
与 N(0)
在最常见的预处理器实现中得到不同的行为?在 M
的替换中,CAT
的第二次调用完全由 CAT
的第一次调用产生的标记组成:
So, why do we get different behavior for M(0)
than for N(0)
with most common preprocessor implementations? In the replacement of M
, the second invocation of CAT
consists entirely of tokens resulting from the first invocation of CAT
:
M(0)
CAT(M_, 0)
CAT_I(M_, 0)
M_0
CAT(x, y)
如果将 M_0
定义为替换为 CAT(M, 0)
,则替换将无限递归.预处理器规范明确禁止这种严格递归".通过停止宏替换来替换,因此 CAT(x, y)
不是宏替换.
If M_0
was instead defined to be replaced by CAT(M, 0)
, replacement would recurse infinitely. The preprocessor specification explicitly prohibits this "strictly recursive" replacement by stopping macro replacement, so CAT(x, y)
is not macro replaced.
然而,在 N
的替换中,CAT
的第二次调用仅包含 部分 由第一次调用 产生的标记>猫
:
However, in the replacement of N
, the second invocation of CAT
consists only partially of tokens resulting from the first invocation of CAT
:
N(0)
CAT(N_, 0) ()
CAT_I(N_, 0) ()
N_0 ()
CAT(x, y)
CAT_I(x, y)
xy
这里 CAT
的第二次调用部分由第一次调用 CAT
产生的标记形成,部分由其他标记形成,即 ()
N
的替换列表中的 code>.替换不是严格递归的,因此当 CAT
的第二次调用被替换时,它不会产生无限递归.
Here the second invocation of CAT
is formed partially from tokens resulting from the first invocation of CAT
and partially from other tokens, namely the ()
from the replacement list of N
. The replacement is not strictly recursive and thus when the second invocation of CAT
is replaced, it cannot yield infinite recursion.
这篇关于C 预处理器,递归宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!