为什么C语言允许用户创建与已有库函数名称相同的宏?

问题描述 投票:-1回答:1
# include <stdio.h> 
# define scanf  "%s Hello World" 
int main (void) 
{ 
   printf(scanf, scanf); 
   getchar(); 
   return 0; 
}

在上面的代码片段中,在代码执行之前,发生了宏扩展阶段。在这个阶段,每次出现的'scanf'都会被替换为 "%s Hello World".

因此 printf(scanf,scanf) 将成为printf(“%s Hello World” , “%s Hello World”).

最后的输出结果将是.NET。

%s Hello World Hello World

现在,在这个程序中没有遇到任何问题,因为我们在这里没有使用scanf函数。

但是,如果我们为了某些目的而使用scanf函数的话

# include <stdio.h> 
# define scanf  "%s Hello World" 
int main(void) 
{ 
   int x;
   printf(scanf, scanf); 
   scanf("%d",&x);
   getchar(); 
   return 0; 

}

我们遇到了一个错误。

main.c: In function ‘main’:
main.c:2:17: error: called object is not a function or function pointer
 # define scanf  "%s Hello World" 
                 ^
main.c:7:4: note: in expansion of macro ‘scanf’
    scanf("%d",&x);
    ^~~~~

为什么C语言允许我们用Scanf这样的库函数来命名宏?

c
1个回答
5
投票

C 2018 7.1.3 1说。

......以下任何一个子项(包括未来的库方向)中列出的每个具有文件范围的标识符都保留作为宏名使用,如果包括其任何一个相关的头文件,则作为同一名称空间中具有文件范围的标识符。

scanf 是一个标识符,它的文件范围列在下面的子项(7.21)中,你包含了它的头, ,所以它被保留作为宏名使用。

7.1.3 2说。

...如果程序在保留标识符的上下文中声明或定义标识符(7.1.4允许的情况除外),或者将保留的标识符定义为宏名,则该行为是未定义的。

综合这些规则,这些规则表明,如果你定义了 scanf 作为宏名称,并包括 <stdio.h>C标准并没有对发生的事情提出任何要求--它并没有被指定为程序中的错误,编译器也没有被要求拒绝你的程序为不正确。编译器也是 要求不拒绝你的程序。

有一个替代方案可以避免这个问题:您可以省略 #include <stdio.h> 并改称 printf (和任何其他功能从 <stdio.h> 的)自己。C标准explicits允许这样做,然后你可以自由地定义一个宏,用于实现 scanf.

这是典型的大部分C标准。它没有 阻止 你做的事情可能会引起问题。特别是,它不要求编译器警告你。C标准这样设计的原因至少有三个(好的或坏的)。

  • 它允许灵活性和扩展性。C被设计成一种可移植的语言,不是指一个程序在任何地方都以同样的方式工作,而是指C可以相对容易地在各种计算平台上实现。C标准允许灵活性,因此语言可以适应一个平台,它允许实现提供标准规定以外的语言扩展。
  • 它减少了编译器的工作负担。通过不对编译器提出很多要求,它使编译器的编写更加容易。(那么,是否对程序实现额外的检查就变成了一个质量问题,而不是遵守C标准的问题。这个政策的效果很好。即使没有standadr的要求,今天广泛使用的编译器也比几十年前的编译器质量高得多)。)
  • 它允许一些在C标准发布之前编写的代码继续使用标准。在有关于使用库函数作为宏名的规则之前,有些程序可能已经写出了这样的程序(程序员可能会这样做,因为他们想在某种程度上改变库函数,至少在外观上)。要求编译器拒绝这些程序,就需要做一些工作,让这些程序与新的语言标准一起工作。
© www.soinside.com 2019 - 2024. All rights reserved.