# 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 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标准这样设计的原因至少有三个(好的或坏的)。