以下C代码中#和##的区别是什么?

时间:2015-11-03 04:02:15

标签: c c-preprocessor

我的C非常生疏,我无法弄清楚这里发生了什么。

#define V_Str(x)                    #x
#define V_FORMAT(a,b,c,d,e)         a ## . ## b ## . ## c ## . ## d ## . ## e 
#define V_PROD_STR                  V_Str(V_FORMAT(13,2,99,44,0) ## 0)

#define xV_Str(x)                   V_Str(x)
#define xV_PROD_STR                 xV_Str(V_FORMAT(13,2,9,44,0) ## 0)

void Test()
{
    printf(" V_PROD_Str(...) = %s\n", V_PROD_STR);
    printf("xV_PROD_Str(...) = %s\n", xV_PROD_STR);
}

输出:

 V_PROD_Str(...) = V_FORMAT(13,2,99,44,0)0
xV_PROD_Str(...) = 13.2.9.44.00

我可以看到#x使得文本字符串的内容与“x”相同,但我很好奇##正在做什么以及为什么在创建版本字符串时使用它。

V_PROD_Str后来用作RC文件中的字符串表,而xV_PROD_Str根本没有使用(我根据自己的理解添加了它)

1 个答案:

答案 0 :(得分:4)

预处理器##运算符是标记并置,在问题中使用它是未定义的行为。

与stringify(#)运算符一样,concatenate运算符处理其参数的未展开值,因此如果您希望它应用于扩展参数,则需要通过另一个宏调用进行间接处理,如同{ {1}}宏,它在对内部宏的调用中扩展其参数。

令牌连接仅在结果是有效令牌时才有效。奇怪的是,xV_Str(x)是一个有效的预处理数字。但是运算符的实际使用是13.2.9.44.0,您无法连接) ## 0)。 (你也不想这样做;意图是连接0的扩展值,但你需要一个间接的宏调用来做到这一点。)

在生成字符串的情况下,标记连接完全没有必要,因为stringify运算符不要求其参数是单个标记。所以以下应该可以正常工作:

V_FORMAT(13,2,9,44,0)