30

如何评估这些宏?

# define i 20
void fun();

int main(){
  printf("%d",i);
  fun();
  printf("%d",i);
  return 0;
}

void fun(){
  #undef i
  #define i 30
}

输出为 2020 年,但是,

# define i 20
void fun(){
  #undef i
  #define i 30
}

int main(){
  printf("%d",i);
  fun();
  printf("%d",i);
  return 0;
}

输出为 3030。请解释。谢谢。

4

7 回答 7

59

C 预处理器从上到下工作,与函数调用无关。无论宏被定义在什么文件中,它从该点(行)开始有效,直到对应undef或直到翻译单元结束。

所以,你的代码会变成,

# define i 20
               // from now on, all token i should become 20
void fun();
int main()
{
  printf("%d",i);   // printf("%d",20);
  fun();
  printf("%d",i);   // printf("%d",20);
  return 0;
}
void fun()
{
#undef i
              // from now on, forget token i
#define i 30
              // from now on, all token i should become 30
}

你的第二个代码会变成,

# define i 20
               // from now on, all token i should become 20
void fun()
{
#undef i
               // from now on, forget i
#define i 30
               // from now on, all token i should become 30
}
int main()
{
  printf("%d",i);    //  printf("%d",30);
  fun();
  printf("%d",i);    // printf("%d",30);
  return 0;
}
于 2013-06-27T17:19:51.140 回答
18

根本没有涉及范围。宏在预处理阶段与编译阶段分开处理,并且没有 C 范围的概念。您的示例可以很容易地是:

#define i 20

void fun();

int main()
{
  printf("%d",i);
  fun();
  printf("%d",i);
  return 0;
}

void fun()
{
}

#undef i
#define i 30

和:

#define i 20
#undef i
#define i 30

void fun()
{
}

int main()
{
  printf("%d",i);
  fun();
  printf("%d",i);
  return 0;
}

您可以从这些中看出为什么它的行为方式如此。

于 2013-06-27T17:20:11.253 回答
7

根本没有范围。

宏由预处理器代替。所以它们的扩展是由它们在源中的位置定义的,从上到下。

于 2013-06-27T17:20:16.093 回答
7

预处理器符号肯定有一个作用域,但该作用域不与文件作用域等其他作用域交互。

预处理器符号范围仅限于单个翻译单元。一个翻译单元中的 A#define与另一个翻译单元无关。

预处理器符号的范围是该符号所在指令之后的标记区域#defines。此后,根据管理规则识别和扩展宏的出现。预处理器宏定义不是递归的。如果替换标记序列包含看起来像正在定义的符号的调用,则这些符号不会被识别。这就是范围在指令之后开始的原因。然而,当一个宏被重新定义时,这仍然是正确的;重新定义是特殊的,必须符合与原始定义相同的规则。(相同的精确规则在标准中)。

预处理器符号的范围以翻译单元的结尾结束,或者如果它受制于#undef指令,则更早。

因此,预处理器符号的范围本质上是翻译单元文本的区域,被视为预处理器标记流,其中该符号有资格被识别和替换。

于 2013-06-27T22:11:16.493 回答
6

预处理器宏没有作用域,因为它们不是 C 语言的一部分。相反,它是一种在编译器正确运行之前运行的搜索替换程序。

预处理器简单地遍历任何文件,不必是 C 源文件,当它找到一个宏调用时,它只是用宏体中的文本替换它。

于 2013-06-27T17:19:51.780 回答
4

宏在编译前的单独阶段对源文本生效。宏在编译后的代码中不再以任何形式存在,并且它们不会在运行时进行评估。

调用宏时(在对源文件进行文本扫描期间)生效的任何宏定义都将被替换到源文本中。

于 2013-06-27T17:20:23.587 回答
2

在 C 预处理器阶段评估宏。C 预处理器阶段与编译阶段分开发生。因此,宏不遵循常规范围。而是按照它们在源文件中出现的顺序(从上到下)对它们进行评估。

在您的第一个代码示例中,它输出 2020,尽管它在 main 函数中调用 fun() 应该将 i 的值更改为 30,但是由于 fun 函数出现在调用它的位置下方,因此该值不会随着预处理器而改变还没有达到那个地步。

在您的第二个代码示例中,它输出 3030 因为 fun 函数位于 main 函数之上。因此,相反的情况发生,因为预处理器已经完成了 fun 函数并将 i 的值更改为 30

于 2013-06-27T20:09:40.513 回答