#define中的##是什么意思?

时间:2011-06-28 08:21:50

标签: c++ c c-preprocessor

这条线是什么意思?特别是##是什么意思?

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

编辑:

有点困惑。没有##的结果会是什么?

6 个答案:

答案 0 :(得分:17)

  

有点困惑。没有##的结果会是什么?

通常你不会注意到任何差异。但 是一个区别。假设Something的类型为:

struct X { int x; };
X Something;

看看:

int X::*p = &X::x;
ANALYZE(x, flag)
ANALYZE(*p, flag)

如果没有令牌连接运算符##,它将扩展为:

#define ANALYZE(variable, flag)     ((Something.variable) & (flag))

((Something. x) & (flag))
((Something. *p) & (flag)) // . and * are not concatenated to one token. syntax error!

使用令牌连接,它会扩展为:

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

((Something.x) & (flag))
((Something.*p) & (flag)) // .* is a newly generated token, now it works!

重要的是要记住,预处理器在预处理器令牌上运行,在文本上运行。因此,如果要连接两个令牌,则必须明确说出来。

答案 1 :(得分:12)

##称为标记连接,用于在宏调用中连接两个标记。

见:

答案 2 :(得分:9)

一个非常重要的部分是这个标记连接遵循一些非常特殊的规则:

e.g。 IBM doc:

  • 连接发生在任何之前 参数中的宏被扩展。
  • 如果连接的结果是a 有效的宏名称,可用于 即使它进一步更换 出现在它的上下文中 通常不可用。
  • 如果多个 ##运算符和/或# 操作员出现在更换中 宏定义列表,订单 对运营商的评估不是 定义

例子也非常自我解释

#define ArgArg(x, y)          x##y
#define ArgText(x)            x##TEXT
#define TextArg(x)            TEXT##x
#define TextText              TEXT##text
#define Jitter                1
#define bug                   2
#define Jitterbug             3

输出:

ArgArg(lady, bug)   "ladybug"
ArgText(con)    "conTEXT"
TextArg(book)   "TEXTbook"
TextText    "TEXTtext"
ArgArg(Jitter, bug)     3

Source是IBM文档。可能与其他编译器有所不同。

到您的专线:

它将变量属性连接到“Something”。并且处理一个逻辑上为anded的变量,如果Something.variable设置了一个标志,则会给出结果。

这是我上次评论和你的问题的一个例子(用g ++编译):

// this one fails with a compiler error
// #define ANALYZE1(variable, flag)     ((Something.##variable) & (flag))
// this one will address Something.a (struct)
#define ANALYZE2(variable, flag)     ((Something.variable) & (flag))
// this one will be Somethinga (global)
#define ANALYZE3(variable, flag)     ((Something##variable) & (flag))
#include <iostream>
using namespace std;

struct something{
int a;
};

int Somethinga = 0;

int main()
{
something Something;
Something.a = 1;

if (ANALYZE2(a,1))
    cout << "Something.a is 1" << endl;
if (!ANALYZE3(a,1))
    cout << "Somethinga is 0" << endl;
        return 1;
};

答案 3 :(得分:3)

根据维基百科

  

令牌连接(也称为令牌粘贴)是C宏预处理器中最微妙且易于滥用的功能之一。使用##预处理器运算符可以将两个参数“粘合”在一起;这允许在预处理的代码中连接两个令牌。这可用于构建复杂的宏,其行为类似于C ++模板的原始版本。

检查Token Concatenation

答案 4 :(得分:3)

让我们考虑一个不同的例子:

考虑

#define MYMACRO(x,y) x##y

没有##,显然预处理器无法将xy视为单独的令牌,可以吗?

在您的示例中,

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))
由于您没有创建任何新标识符,因此根本不需要

##。事实上,编译器发出“错误:粘贴”。“和”变量“没有给出有效的预处理令牌”

答案 5 :(得分:3)

这不是您的问题的答案,只是一篇CW帖子,其中包含一些提示,可帮助您自己探索预处理器。

预处理步骤实际上是在编译任何实际代码之前执行的。换句话说,当编译器开始构建代码时,不会留下 #define 语句或类似内容。

理解预处理器对代码的作用的一个好方法是获取预处理的输出并查看它。

这是针对Windows的方法:

创建一个名为 test.cpp 的简单文件,并将其放在一个文件夹中,例如c:\ temp。 我看起来像这样:

#define dog_suffix( variable_name ) variable_name##dog

int main()
{
  int dog_suffix( my_int ) = 0;
  char dog_suffix( my_char ) = 'a';

  return 0;
}

不是很有用,但很简单。打开Visual Studio命令提示符,导航到该文件夹​​并运行以下命令行:

c:\temp>cl test.cpp /P

因此,这是你的文件运行的编译器(cl.exe),而/ P选项告诉编译器将预处理的输出存储到文件中。

现在在test.cpp旁边的文件夹中你会找到test.i,对我来说就是这样:

#line 1 "test.cpp"


int main()
{
  int my_intdog = 0;
  char my_chardog = 'a';

  return 0;
}

如您所见,没有 #define ,只剩下它扩展的代码。