【发布时间】:2021-02-03 16:47:32
【问题描述】:
C 和 C++ 标准都指定了以下内容:
16.3.1 参数替换 (C++11)
6.10.3.1 参数替换 (C11)
在确定了调用类函数宏的参数后,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后跟 ## 预处理标记(见下文),在其中包含的所有宏都已展开后,将替换为相应的参数。在被替换之前,每个参数的预处理标记都被完全宏替换,就好像它们形成了预处理文件的其余部分一样;没有其他可用的预处理令牌。
可以将本段解释为标准要求:
(1)首先识别宏参数(逗号分隔),然后分别展开每个参数中包含的所有宏,
或
(2) 展开参数列表中包含的所有宏,然后识别每个参数。
为了说明这一点,让我们考虑以下示例代码:
#define CONDITION (0)
#if (CONDITION > 0)
#define FunctionAlias(par_a, par_b, par_opt, par_c) \
FunctionName(par_a, par_b, par_opt, par_c)
#else
#define FunctionAlias(par_a, par_b, par_c) \
FunctionName(par_a, par_b, par_c)
#endif
int global_a, global_b, global_c;
#if (CONDITION > 0)
int global_opt;
#endif
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionAlias(
global_a,
global_b,
#if (CONDITION > 0)
global_opt,
#endif
global_c
);
}
(1) 方法一会产生无效代码:
int global_a, global_b, global_c;
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionName(global_a, global_b, #if ((0) > 0) global_opt);
}
(2) 方法 2 生成有效的 C 代码:
int global_a, global_b, global_c;
void FunctionName(int a, int b, int c)
{
}
void AnotherFunction()
{
FunctionName(global_a, global_b, global_c);
}
对标准的哪些解释是正确的?
【问题讨论】:
-
我向您展示第三种解释:
error: embedding a directive within macro arguments has undefined behavior。这就是 GCC 和 Clang 所做的。该错误发生在宏展开之前。 -
@TedLyngmo 谢谢,完成。
-
@HolyBlackCat 实际上,GCC 9.3.0 正在编译此代码而没有任何抱怨。无论如何,感谢您的评论 - 我改写了标题以更准确。
-
添加
-pedantic-errors,它使GCC更符合标准。 -
(历史记录:某些版本的 GCC,大约 3.0,确实使 UB 的这种特殊情况成为一个硬错误。我不记得它是否曾经以这种方式发布,但我们得到了很多抱怨来自那些对我们破坏他们的代码感到恼火的人......因为他们将
#ifdefs 放在printf的参数中,并且一些C 库将printf设为宏。所以我们把它降为警告。)
标签: c++ c c-preprocessor standards preprocessor