为何MSVC展开C预处理器宏A比GNU/Clang多一次扫描?
MSVC与GNU/Clang预处理器宏展开差异的原因
先看问题中的代码示例:
#include <stdio.h> #define EMPTY() #define DEFER_(X) X EMPTY() #define DEFER(...) __VA_ARGS__ DEFER_(EMPTY)() #define TO_STR(X) TO_STR_(X) #define TO_STR_(X) #X #define A() 123 #define EXPAND(...) __VA_ARGS__ #define EXPAND1(...) EXPAND(__VA_ARGS__) #define EXPAND2(...) EXPAND1(__VA_ARGS__) int main(void) { printf(TO_STR(EXPAND1(DEFER(A)()))"\n"); }
这段代码通过DEFER宏延迟A()的展开,再用EXPAND系列宏触发展开。在GNU/Clang下,EXPAND1(DEFER(A)())最终会展开为123,但MSVC下只会得到A(),必须用EXPAND2才能得到123——两者的差异根源在于预处理器的扫描规则不同:
GNU/Clang遵循C标准的多轮扫描规则:在宏替换过程中,替换列表展开后,预处理器会反复扫描整个结果,查找所有可展开的宏(正在展开的自身宏除外,避免递归),直到没有新的宏可以展开为止。比如处理
EXPAND(DEFER(A)())时,DEFER(A)先展开为A(),此时预处理器会立即扫描到A()并将其展开为123。MSVC采用单次扫描策略:每完成一次宏替换,预处理器仅对替换结果做一轮扫描,不会反复检查剩余的可展开宏。也就是说,
EXPAND(DEFER(A)())展开得到A()后,这一轮扫描就结束了,不会继续展开A();必须再经过一次EXPAND调用,才能触发A()的展开。
本质上,MSVC的预处理器没有完全严格遵循C标准中关于宏替换后重新扫描的要求,而GNU/Clang则严格实现了标准规定的多轮扫描逻辑,这就导致了展开A()时需要的扫描次数差异。
内容的提问来源于stack exchange,提问作者BlackBloodyRabbit
相关产品推荐
相关产品推荐

