了解令牌粘贴

Understanding token pasting

请考虑以下代码片段,

#include<stdio.h>
#define AB "HELLO"
#define A "WORLD"
#define MAC1 A##B
void main(void)
{
    printf(MAC1"\n");
}

编译执行后打印HELLO。 请帮助理解为什么 ##AB 之间被忽略。

同样在编译以下代码时,给出以下错误,

#include<stdio.h>
#define AB "HELLO"
#define A "WORLD"
#define MAC2 A#B
void main(void)
{
    printf(MAC2"\n");
}

21.c: In function âmainâ:
21.c:11:2: error: stray â#â in program
21.c:11:9: error: expected â)â before string constant

为什么会出现这种行为?

标记#和##在宏扩展中是特殊的。 '##' 是令牌粘贴运算符。它在宏中获取 2 个符号并将它们绑定为一个符号。这对于创建专用名称很有用。

#define LINE_COUNTER   static int line## __LINE__ =0; line ## __LINE__ ++;

这会创建一个变量,该变量会在该行执行时递增。它可以在同一个函数中多次使用,因为它有一个基于行号的唯一名称。 ( static int line_17 = 0; line_17 ++; )

“#”符号是字符串化运算符。它将后面的内容变成字符串。

#define assert( x )  if( ! x ) { fprintf( stderr, "Assertion failed %s\n", #x ); exit(  1 ) }

## 连接符号。换句话说,A##B 表示不存在的 AB。 如果你想做一些事情,比如从 2​​ 个变量名中创建一个变量名,你可以这样使用:

#define newVar(x,y) x##y

int main()
{
    int newVar(my,Var); // int myVar;
    newVar(f,printf)(stdout,"Hello World"); // fprintf(stdout,"Hello World");
}

# 取一个名字,然后把它变成一个字符串。例如

#define VARSTR(x) #x

int main()
{
    printf(VARSTR(myVar));
}

打印 "myVar" 到字符串(即使 myVar 不是 main 的变量,它在宏替换中)

 #define "HELLO" "WORLD"

要连接字符串文字,只需将它们并排放置,中间没有其他有效标记。所以这导致 "HELLOWORLD" 请注意,文字之间的白色 space 被忽略,并且是不必要的。我用它来澄清它。

这将像这样替换 AA 和 BB :

#include<stdio.h>
#define AA "HELLO"
#define BB "WORLD"
#define MAC1 AA BB
void main(void)
{
    printf(MAC1"\n"); // printf("HELLO" "WORLD""\n");
    // same as printf("HELLOWORLD\n");
}

## 预处理令牌用于 令牌粘贴

宏定义中 A##B 的作用是生成标记 AB。所以 printf(MAC1"\n")printf(AB"\n") 相同。

令牌不是 "ignored",它正在发挥作用。


在你的第二个例子中,A#B 只是字面上的意思 A#B# 仅在类似函数的宏中具有特殊含义。所以你的代码扩展为:

printf(A#B"\n");

这是一个错误,因为 # 不是 C 语法的一部分,在预处理器之外。

正如其他人指出的那样,## 运算符用于创建新令牌。 #define MAC1 A##B 将创建定义为包含字符串 HELLO 的宏 AB。这就是 printf(MAC1"\n") 打印 "HELLO" 的原因。如果要拼接ABA表示的字符串可以定义MAC1如下:

#define MAC1 AB A

你的预处理器将首先用 "HELLO""WORLD 替换 AB B 所以 MAC1 看起来像

#define MAC1 "HELLO""WORLD"

然后它会替换 MAC1 它出现的所有地方。所以你的 printf 语句将是

printf("HELLO""WORLD""\n"); // --> HELLOWORLD