C预处理器如何处理循环依赖?

时间:2014-06-12 06:08:34

标签: c linux gcc c-preprocessor

我想知道 C 预处理器如何处理循环依赖(#defines)。这是我的计划:

#define ONE TWO 
#define TWO THREE
#define THREE ONE

int main()
{
    int ONE, TWO, THREE;
    ONE = 1;
    TWO = 2;
    THREE = 3;
    printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE,  TWO, THREE);
}

这是预处理器输出。我无法弄清楚输出为何如此。我想知道预处理器在这种情况下采取的各种步骤,以提供以下输出。

# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"

int main()
{
 int ONE, TWO, THREE;
 ONE = 1;
 TWO = 2;
 THREE = 3;
 printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE, TWO, THREE);
}

我在linux 3.2.0-49-generic-pae上运行这个程序,并在gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)中编译。

5 个答案:

答案 0 :(得分:76)

在展开预处理器宏时,不会展开该宏的名称。因此,所有三个符号都被定义为自己:

ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO (        "                         TWO      "        )
THREE -> ONE -> TWO -> THREE (      "                         THREE    "        )

此行为由C标准的§6.10.3.4设定(C11草案中的部分编号,尽管据我所知,该部分的措辞和编号自C89起未改变)。遇到宏名称时,会将其替换为其定义(并处理###预处理程序运算符,以及类函数宏的参数。然后重新扫描结果以获取更多宏(在文件的其余部分的上下文中):

  

2 /如果在替换列表的扫描期间找到要替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它。此外,如果任何嵌套替换遇到要替换的宏的名称,则不会替换它......

该条款接着说,任何因递归调用而未被替换的令牌都被“冻结”:它永远不会被替换:

  

...这些无法替换的宏名称预处理令牌不再可用于进一步替换,即使它们稍后(重新)检查其中宏名称预处理令牌将被替换的上下文中。

最后一句所指的情况很少在实践中出现,但这是我能想到的最简单的案例:

#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)

结果是one, two。在替换two期间,one,two已扩展为a,展开的two被标记为完全展开。随后,b(one,two)被扩展。这不再是替换two的上下文,但是two的第二个参数b已被冻结,因此它不会再次展开。

答案 1 :(得分:17)

您的问题已由出版物ISO/IEC 9899:TC2第6.10.3.4节“重新扫描和进一步更换”第2段回答,我在此引用为方便起见;在将来,如果您对规范有疑问,请考虑阅读具体说明

  

如果在替换列表的扫描期间找到要替换的宏的名称   (不包括源文件的其余预处理标记),它不会被替换。   此外,如果任何嵌套替换遇到要替换的宏的名称,   它没有被替换。这些未替换的宏名称预处理标记不再存在   可用于进一步更换,即使它们稍后(重新)检查在其中   否则,宏名称预处理令牌将被替换。

答案 2 :(得分:9)

https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros回答了有关自引用宏的问题。

答案的关键在于,当预处理器找到自引用宏时,它根本不会扩展它们。

我怀疑,使用相同的逻辑来防止循环定义的宏的扩展。否则,预处理器将处于无限扩展状态。

答案 3 :(得分:4)

在您的示例中,您在定义之前执行宏处理 变量名称相同,所以不管结果如何 宏处理是,你总是打印1, 2, 3

以下是首先定义变量的示例:

#include <stdio.h>
int main()
{
    int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
    printf("%d\n", A);
    printf("%d\n", B);
    printf("%d\n", C);
}

这会打印3 3 3。有点隐蔽,取消评论#define C A会改变行printf("%d\n", B);的行为

答案 4 :(得分:4)

以下是rici'sEric Lippert's答案中描述的行为的一个很好的演示,即如果在扩展同一个宏时再次遇到宏名称,则不会重新展开宏名称。

test.c的内容:

#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE

int foo[] = {
  ONE,
  TWO,
  THREE
};

gcc -E test.c的输出(不包括初始# 1 ...行):

int foo[] = {
  1, 2, 3, ONE,
  2, 3, 1, TWO,
  3, 1, 2, THREE
};

(我会将此作为评论发布,但在评论中包含大量代码块有点尴尬,所以我将其作为社区Wiki答案而不是。如果你觉得它会更好地包括在内现有答案,随意复制并要求我删除此CW版本。)