这行代码的意思是什么?特别是,##
是什么意思?
#define ANALYZE(variable, flag) ((Something.##variable) & (flag))
编辑:
还是有点困惑。如果没有 ##
,结果会是什么样子?
这行代码的意思是什么?特别是,##
是什么意思?
#define ANALYZE(variable, flag) ((Something.##variable) & (flag))
编辑:
还是有点困惑。如果没有 ##
,结果会是什么样子?
有点困惑。没有 ## 的话结果会是什么?
通常情况下,您不会注意到任何差异。但是确实有一个区别。假设Something
是以下类型:
struct X { int x; };
X Something;
再看一下:
int X::*p = &X::x;
ANALYZE(x, flag)
ANALYZE(*p, flag)
没有 token 连接操作符 ##
,它会展开为:
#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!
记住,预处理器操作的是预处理器标记,而不是文本。所以如果你想连接两个标记,你必须明确地说明。
其中一个非常重要的部分是,这个标记串联遵循一些非常特殊的规则:
例如 IBM 文档:
示例也非常自我解释
#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
这段内容的来源是IBM文档。在其他编译器中可能会有所不同。
针对您的代码行:
它将变量属性连接到“Something.”并寻址逻辑与运算后的变量,如果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;
};
##
的情况下,结果会是什么? - Dante May Codea.##b
。a##b
右侧求值为全局变量。a.b
也右侧求值为结构体实例 a 内的变量。 - fyr预处理步骤实际上是在任何实际代码被编译之前执行的。换句话说,当编译器开始构建你的代码时,没有留下任何#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了,只剩下它所展开的代码。
#define MYMACRO(x,y) x##y
##
,预处理器就不能将x
和y
作为不同的标记来看待,对吗?#define ANALYZE(variable, flag) ((Something.##variable) & (flag))
##
并不需要,因为您没有创建新的标识符。实际上,编译器会发出“错误:拼接'.'和'variable'不会产生有效的预处理令牌”。
根据维基百科
标记串联,也称为标记粘合,是C宏预处理器中最微妙且容易被滥用的功能之一。使用##预处理器运算符可以将两个参数“粘合”在一起;这允许在预处理代码中连接两个标记。这可以用于构建复杂的宏,其作用类似于C ++模板的原始版本。
请查看标记串联。