为什么我需要宏的双层间接寻址?

问题描述:

在:C++ FAQ - Miscellaneous technical issues - [39.6] What should be done with macros that need to paste two tokens together?为什么我需要宏的双层间接寻址?

有人能向我解释为什么?我读的全部是相信我,但我不能仅仅因为有人这样说就信任某些东西。

我想尽了办法,我无法找到任何错误出现:

#define mymacro(a) int a ## __LINE__ 
mymacro(prefix) = 5; 
mymacro(__LINE__) = 5; 
int test = prefix__LINE__*__LINE____LINE__; // fine 

那么为什么做我需要做的是这样的,而不是(引自网页):

但是,当您使用##时,您需要双层间接寻址。 基本上你需要创建一个“标记粘贴”等 作为一个特殊的宏:

#define NAME2(a,b)   NAME2_HIDDEN(a,b) 
#define NAME2_HIDDEN(a,b) a ## b 

相信我这一点 - 你真的需要做 这个! (和请人写信给我说,有时候工作没有 间接的第二层尝试串联一个符号以 __ LINE__看看然后会发生什么。)

编辑:可能有人也解释了为什么他使用NAME2_HIDDEN在下面声明之前?在我使用它之前,定义NAME2_HIDDEN宏似乎更符合逻辑。这里有一些诡计吗?

+0

我不知道我明白你在问什么... –

+0

我看到它有点不清楚,我会编辑。 – Rookie

+0

@tenfour,完成。你可能知道我编辑部分的答案吗? – Rookie

的C规格的相关部分:

6.10.3.1参数替换

参数的函数宏的调用后已经确定, 参数替换发生。在替换列表中的参数,除非前面由# 或##预处理记号或后跟一个##预处理标记(见下文),是 由对应的参数替换后其中所含的所有宏已经 扩大。在被替换之前,每个参数的预处理标记是 完全宏替换,就好像它们构成了预处理文件的其余部分一样;没有其他 预处理令牌可用。

,决定是否要双间接与否的关键部分是第二句和其中的例外 - 如果该参数参与###操作(如mymacroNAME2_HIDDEN的PARAMS ),那么在执行###之前,参数中的任何其他宏都不会被展开。另一方面,如果宏体中没有###(与NAME2一样),则扩展参数中的其他宏。

所以它归结为你想要的 - 有时你希望所有的宏扩展FIRST,然后做###(在这种情况下,你想要双层间接寻址),有时你不希望先扩展宏(在这种情况下,您无法使用双层宏,因此您需要直接执行此操作。)

+1

。我发现它非常令人不安,网站告诉我总是使用它......而在我的情况下,我从来不想以这种方式使用它。 – Rookie

__LINE__是应该解析为当前行号的特殊宏。当你做一个记号膏__LINE__直接,但是,它并没有得到一个机会来解决,所以你最终与令牌prefix__LINE__代替,比如说,prefix23,就像你可能会被期待,如果你会写这个代码狂野。

+0

编辑:哦,等等,你的意思是有人会希望给变量名称的行号?好吧。那会导致所有问题呢? – Rookie

+0

你是否也可以解释他为什么以错误的顺序使用宏?例如。他在声明之前引用宏'NAME2_HIDDEN'。这被认为是很好的做法,或者它是一种伎俩?我测试了两种方式,我似乎得到相同的结果。 – Rookie

+0

您可以按任意顺序定义宏,这两个都不是错的。确实是 –

Chris Dodd对您问题的第一部分有很好的解释。至于第二部分,关于定义序列,简短的版本是#define指令本身根本不被评估;只有在文件中的其他位置找到符号时才会对它们进行评估和扩展。例如:

#define A a //adds A->a to the symbol table 
#define B b //adds B->b to the symbol table 

int A; 

#undef A  //removes A->a from the symbol table 
#define A B //adds A->B to the symbol table 

int A; 

第一int A;变得int a;,因为这是如何A在文件中的该点被定义。两次扩展后第二个int A;变为int b;。它首先扩展为int B;,因为A在文件中的那个点被定义为B。然后预处理器识别B是检查符号表时的宏。 B然后扩展为b

唯一重要的是符号在扩展点的定义,而不管定义在哪里。

+1

噢,我只是觉得把它们按顺序放在你使用它们的位置会更合乎逻辑,不知何故对我更有意义。感谢您的解释。 – Rookie

声明宏的顺序并不重要,它们的使用顺序是。如果你在声明之前真的使用这个宏 - (在实际的代码中,不是在宏被保持休眠状态直到被召唤),那么你会得到一个错误的种类,但因为大多数理智的人不去做这些类型的东西,写一个宏,然后编写一个函数,使用一个尚未定义的宏,等等,等等......看起来你的问题不仅仅是一个问题,而是我只回答那一部分。我认为你应该把这一点再打破一点。

我从这里的所有链接中收集到的最不是非技术性的答案,以及链接的链接;)是单层间接macro(x) #x将输入的宏名称串化,但是通过使用双层,它会将输入宏观的价值。

#define valueOfPi 3 
#define macroHlp(x) #x 
#define macro(x) macroHlp(x) 
#define myVarOneLayer "Apprx. value of pi = " macroHlp(valueOfPi) 
#define myVarTwoLayers "Apprx. value of pi = " macro(valueOfPi) 

printf(myVarOneLayer); // out: Apprx. value of pi = valueOfPi 
printf(myVarOTwoLayers); // out: Apprx. value of pi = 3 

printf(myVarOneLayer)

printf(myVarOneLayer)会发生什么情况扩展到printf("Apprx. value of pi = " macroHlp(valueOfPi))

macroHlp(valueOfPi)试图字符串化输入,输入本身不进行评估。生活中唯一的目的是接受输入并进行串联。因此,它扩展为"valueOfPi"

所以,发生在printf(myVarTwoLayers)

printf(myVarTwoLayers)什么是扩大到printf("Apprx. value of pi = " macro(valueOfPi)

macro(valueOfPi)没有字符串化操作,即不存在#x在它的扩张,但有一个x,所以它必须评估x并将值输入到macroHlp进行串化。它扩大到macroHlp(3)这反过来将串数3,因为它使用#x