早上在写代码时遇到这样一个问题:即如何在一个拥有多行的宏定义中做注释?,这里把方法演化的过程贴出来,可能对某些朋友有些借鉴意义。

宏定义高深莫测,而且是比较细节的东西,详细说明请参见"C参考手册"之类的书籍。

在我的代码中,我大致要做这样一个简单的事情:printf("%s%s%s\n", "hello", "macro", "yeah!"); "%s%s%s\n"这个字符串中每一项输出都有一定的含义,而且在真实代码里,这个串中的输出项可不止3个,所以一个直接的想法就是将其定义为一个宏。

#define STR_OUTPUT_FORMAT_V0  "%s%s%s\n"
printf(STR_OUTPUT_FORMAT_V0, "hello ", "macro, ", "yeah!");
程序输出:hello macro, yeah!

由于真实代码中这个串很长,所以打算美化一下格式,定义成下面的样子:
#define STR_OUTPUT_FORMAT_V1  "%s\
                               %s\
                               %s\n"
printf(STR_OUTPUT_FORMAT_V1, "hello ", "macro, ", "yeah!");
程序输出:hello                                macro,                                yeah!

这样的定义显然不对,也在我意料之中,续行符将空格也续到格式串中了,导致输出的结果中带有大量空格。

改进一下,利用C语言的字符串自动连接语法。
#define STR_OUTPUT_FORMAT_V2  "%s"\
                              "%s"\
                              "%s\n"
printf(STR_OUTPUT_FORMAT_V2, "hello ", "macro, ", "yeah!");
程序输出:hello macro, yeah!

现在的问题是如何在这样一个多行的宏定义里加入注释,字段含义特殊,加上注释有利于以后维护以及别人阅读你的代码,否则一堆%s%s,让人看了就头痛。先这么加试试:
#define STR_OUTPUT_FORMAT_E1  "%s"\   /* comment1 */
                              "%s"\   /* comment2 */
                              "%s\n"  /* comment3 */
printf(STR_OUTPUT_FORMAT_E1, "hello ", "macro, ", "yeah!");
我们得到的结果:编译错误。
通过gcc -E 选项我们看到,宏替换后的代码:
                              "%s"\
                              "%s\n"
int main() {
        printf("%s"\, "hello ", "macro, ", "yeah!");
}

由于没有续行符在注释前面,所以宏定义的后两行实际上并没有被包含在宏定义中,就像没有暂住证的人一样,被GCC这个"警察"逮个正着。

继续改进:
#define STR_OUTPUT_FORMAT_V3  "%s"   /* comment1 */ \
                              "%s"   /* comment2 */ \
                              "%s\n"  /* comment3 */
printf(STR_OUTPUT_FORMAT_V3, "hello ", "macro, ", "yeah!");
程序输出:hello macro, yeah!
显然预编译器忽略宏定义中的注释以及空格,STR_OUTPUT_FORMAT_V3就完全符合我的要求了。

当然,很多人不建议使用宏,特别是C++的Fans,宏也的确有很多弊端,这里也有替代方法:
const char *str_output_format = "%s"    /* comment1 */
                                 "%s"    /* comment2 */
                                 "%s\n"; /* comment3 */
printf(str_output_format, "hello ", "macro, ", "yeah!");
程序输出:hello macro, yeah!

用一个字符串变量代替格式宏,还可以避免上述由于在宏中做注释带来的一系列问题。