【问题标题】:Scope of macros in C?C中的宏范围?
【发布时间】:2013-06-25 07:44:42
【问题描述】:

如何评估这些宏?

# 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。 请解释。谢谢。

【问题讨论】:

  • 大多数编译器都有一个开关,可以让你看到预处理器的输出(宏扩展和编译之间的步骤),我建议你尝试一下
  • 基本上:C 和 C 预处理器是完全不相关的语言。

标签: c c-preprocessor


【解决方案1】:

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;
}

【讨论】:

  • 请注意,尽管其他答案断言没有范围,但 C 预处理器宏确实具有明确定义的范围,但该范围仅与预处理阶段相关,与任何其他翻译阶段。该范围是“从定义点到当前翻译单元结束,或者直到使用#undefine 未定义宏或使用#define 重新定义宏。”
【解决方案2】:

根本不涉及范围。宏在预处理阶段与编译阶段分开处理,并且没有 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;
}

你可以从这些中看出它为什么会这样。

【讨论】:

  • 预处理可能在语义上与语言的其他部分分开,但不一定与编译器分开。 GCC 多年前集成了它。
  • 真;我真的只是从逻辑意义上说。不过,这是一个很好的说明。
【解决方案3】:

根本没有范围。

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

    • +1 指出宏有一个作用域,只是与 C 语言的作用域不同。
    【解决方案5】:

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

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

    【讨论】:

    • 预处理器宏是 C 语言的一部分,在 C 2011 第 6.10.3 条中。是否单独进行预处理是一个实现细节;一些编译器集成了它。
    • 如果没有范围,那么假设我有 file1.c 其中包含宏定义 #define SOME_MACRO 100 我可以直接在 file2.c?我想我在链接器阶段给出了错误,那么宏没有范围是什么意思?
    • @user2520119 我的意思是,在函数内部、函数内部、函数内部或函数之间或函数外部定义宏并不重要。所有宏在定义后都是全局的,但仅在定义它的翻译单元中。
    • 那么这段代码呢? void func1(void) { printf("func1 : SOME_MACRO : %s\n",SOME_MACRO); } #define SOME_MACRO "I ma here" void func2(void) { printf("func2 : SOME_MACRO : %s\n",SOME_MACRO); } int main() { func1(); func2(); return 0; }
    • @user2520119 这会导致错误,因为SOME_MACRO isn't defined when you attempt to use it in func1`。
    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 1970-01-01
        • 2018-01-02
        相关资源
        最近更新 更多