为什么C语言允许用户创建与预先存在的库函数名称相同的宏?

为什么C语言允许用户创建与预先存在的库函数名称相同的宏?,c,macros,language-lawyer,c-preprocessor,C,Macros,Language Lawyer,C Preprocessor,现在,在这个程序中没有遇到任何问题,因为我们没有在这里使用scanf函数 但是,如果我们出于某种目的使用scanf函数 %s Hello World Hello World 那么为什么C首先允许我们以库函数(如Scanf)命名宏呢?C 2018 7.1.3 1说: …以下任何子条款(包括未来的库说明)中列出的每个文件范围标识符都保留为用作宏名称,并且如果包含其任何关联头,则保留为具有相同名称空间中文件范围的标识符 scanf是一个标识符,其文件范围在下面的子条款(7.21)中列出,并且您包括它

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

但是,如果我们出于某种目的使用scanf函数

%s Hello World Hello World
那么为什么C首先允许我们以库函数(如Scanf)命名宏呢?

C 2018 7.1.3 1说:

…以下任何子条款(包括未来的库说明)中列出的每个文件范围标识符都保留为用作宏名称,并且如果包含其任何关联头,则保留为具有相同名称空间中文件范围的标识符

scanf
是一个标识符,其文件范围在下面的子条款(7.21)中列出,并且您包括它的头,因此它保留用作宏名称

7.1.3.2表示:

…如果程序在其保留的上下文中声明或定义标识符(7.1.4允许的除外),或将保留标识符定义为宏名称,则行为未定义

结合起来,这些规则表明,如果您将
scanf
定义为宏名称并包含
,则C标准不会对发生的情况施加任何要求,因为它未指定为程序中的错误,也不要求编译器以错误为由拒绝您的程序。编译器也被要求不拒绝您的程序

有一种方法可以避免这个问题:您可以省略
#include
,而是自己声明
printf
(以及您使用的
中的任何其他函数)。C标准显式命令允许这样做,然后您可以为
scanf
自由定义宏

这是大多数C标准的典型特征:它不会阻止您做可能导致问题的事情。特别是,它不需要编译器警告您。C标准的设计至少有三个原因(好或坏):

  • 它允许灵活性和扩展性。C被设计成一种可移植语言,并不是说程序在任何地方都以同样的方式工作,而是说C可以相对容易地在各种计算平台上实现。C标准允许灵活性,因此语言可以根据平台进行调整,并且它允许实现为语言提供超出标准规定的扩展
  • 它减少了编译器的工作量。由于没有对编译器强加太多要求,因此编写编译器更容易。(那么,是否对程序实施额外检查就成了一个质量问题,而不是遵守C标准的问题。这一政策运作良好。即使没有standadr的要求,今天广泛使用的编译器的质量也比几十年前要高很多。)
  • 它允许在C标准发布之前编写一些代码来继续使用该标准。在有关于使用库函数作为宏名称的规则之前,可能已经编写了一些这样做的程序(程序员可能会这样做,因为他们希望在某种程度上改变库函数,至少在外观上是这样)。要求编译器拒绝这些程序需要做一些工作才能使这些程序使用新的语言标准

因为它。如果它确实是一个恼人的限制,因为人们使用预处理器指令来替换所有的函数调用。我知道这个问题是关于C的,但是现代C++回答是“因为宏杀死小狗”。乔治,如果你需要杀死小狗来达到你的目标,那么,然后你杀了他们。看看各种各样的预处理器魔法,比如我正在学习的谷歌测试。谢谢你的回答。现在我很明白其中的原因。我特别想感谢你详细地写下这个解释,因为出于某些原因,在这个网站上,询问一个真正的疑问会立即被大量的反对票所淹没。如果没有人回答这个问题,我也没问题,但出于某种原因,人们会拒绝投票并报告,即使这个问题符合所有规则。我在发帖前检查了这个问题是否重复(不是),并在谷歌上搜索了一下。无论如何,非常感谢您帮助我,请注意,其中一个名称仅在“包含其关联的任何标题”时保留。在
scanf
的情况下,这种异常不太可能发生,因为几乎总是包含
stdio.h
。但对于向后兼容性来说,例外情况很重要。它允许定义新的头,而不呈现无效的旧程序,这些程序碰巧有新头保留的名称的私有用途。
%s Hello World Hello World
# 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);
    ^~~~~