mrn*_*mrn 2 c c++ standards preprocessor c-preprocessor
C 和 C++ 标准都指定了以下内容:
\n\n\n16.3.1 参数替换 (C++11)
\n6.10.3.1 参数替换 (C11)
\n在识别出调用类似函数的宏的参数后,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后面有 ## 预处理标记(见下文),否则在其中包含的所有宏都已展开后将被相应的参数替换。在被替换之前,每个参数\xe2\x80\x99s预处理标记被完全宏替换,就好像它们形成了预处理文件的其余部分一样;没有其他可用的预处理标记。
\n
人们可以将本段解释为标准要求:
\n(1) 首先识别宏参数(以逗号分隔),然后分别展开每个参数中包含的所有宏,
\n或者
\n(2) 展开参数列表中包含的所有宏,然后识别每个参数。
\n为了说明这一点,让我们考虑以下示例代码:
\n#define CONDITION (0)\n\n#if (CONDITION > 0)\n#define FunctionAlias(par_a, par_b, par_opt, par_c) \\\n FunctionName(par_a, par_b, par_opt, par_c)\n#else\n#define FunctionAlias(par_a, par_b, par_c) \\\n FunctionName(par_a, par_b, par_c)\n#endif\n\nint global_a, global_b, global_c;\n#if (CONDITION > 0)\nint global_opt;\n#endif\n\nvoid FunctionName(int a, int b, int c)\n{\n}\n \nvoid AnotherFunction()\n{\n FunctionAlias(\n global_a,\n global_b,\n #if (CONDITION > 0)\n global_opt,\n #endif\n global_c\n );\n}\nRun Code Online (Sandbox Code Playgroud)\n(1) 方法一会产生无效代码:
\nint global_a, global_b, global_c;\n\nvoid FunctionName(int a, int b, int c)\n{\n}\n\nvoid AnotherFunction()\n{\n FunctionName(global_a, global_b, #if ((0) > 0) global_opt);\n}\nRun Code Online (Sandbox Code Playgroud)\n(2) 方法 2 生成有效的 C 代码:
\nint global_a, global_b, global_c;\n\nvoid FunctionName(int a, int b, int c)\n{\n}\n\nvoid AnotherFunction()\n{\n FunctionName(global_a, global_b, global_c);\n}\nRun Code Online (Sandbox Code Playgroud)\n对标准的哪种解释是正确的?
\n首先,您根本不能将预处理指令放入类似函数的宏的参数中,因为某些文本与您引用的内容略有不同:
\n\n\n\n如果参数列表中存在预处理标记序列,否则这些预处理标记将充当预处理指令,则行为未定义。
\n
其次,与此无关,该标准要求您称为 (1) 的行为。这是由您引用的这部分文本指定的:
\n\n\n在被替换之前,每个参数\xe2\x80\x99s预处理标记被完全宏替换,就好像它们形成了预处理文件的其余部分一样;没有其他可用的预处理标记。
\n
如果在识别参数之间的边界之前扩展类似函数的宏的参数,则这句话没有任何意义。您还可以通过实验看到这一点,只需稍微修改一下您的代码:
\n#if (CONDITION > 0)\n#define FunctionAlias(par_a, par_b, par_opt, par_c) \\\n FunctionName(par_a, par_b, par_opt, par_c)\n#else\n#define FunctionAlias(par_a, par_b, par_c) \\\n FunctionName(par_a, par_b, par_c)\n#endif\n\nint global_a, global_b, global_c;\n#if (CONDITION > 0)\nint global_opt;\n#define GLOBAL_OPT global_opt,\n#else\n#define GLOBAL_OPT /*nothing*/\n#endif\n\nvoid FunctionName(int a, int b, \n#if CONDITION > 0\n int opt,\n#endif\n int c)\n{\n}\n \nvoid AnotherFunction()\n{\n FunctionAlias(\n global_a,\n global_b,\n GLOBAL_OPT\n global_c\n );\n}\nRun Code Online (Sandbox Code Playgroud)\nCONDITION如果未定义或为零,则可以正常编译,但是当CONDITION非零时,您将收到如下错误
test.c: In function \xe2\x80\x98AnotherFunction\xe2\x80\x99:\ntest.c:28:17: error: macro "FunctionAlias" requires 4 arguments, but only 3 given\n 28 | );\n | ^\nRun Code Online (Sandbox Code Playgroud)\n证明在寻找 的四个参数之前GLOBAL_OPT没有展开FunctionAlias。