Akk*_*619 66 c compiler-construction printf scanf
从下面的代码片段中可以看出,我已经声明了一个char变量和一个int变量.当代码被编译,就必须确定变量的数据类型str和i.
为什么我需要扫描我的变量,它是通过指定的字符串或整型变量时再次告诉%s或%d给scanf?当我声明我的变量时,编译器是否足够成熟以确定?
#include <stdio.h>
int main ()
{
char str [80];
int i;
printf ("Enter your family name: ");
scanf ("%s",str);
printf ("Enter your age: ");
scanf ("%d",&i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Yu *_*Hao 120
因为变量参数函数没有可移植的方式,scanf并且printf知道变量参数的类型,甚至没有传递多少个参数.
请参阅C FAQ:如何发现实际调用函数的参数数量?
这就是必须至少有一个固定参数来确定变量参数的数量和类型的原因.而这个论点(标准称之为parmN,参见C11(ISO/IEC 9899: 201x)§7.16 变量参数)扮演这个特殊角色,并将传递给宏va_start.换句话说,你不能在标准C中使用这样的原型函数:
void foo(...);
Run Code Online (Sandbox Code Playgroud)
Dev*_*lus 29
编译器无法提供必要信息的原因很简单,因为此处不涉及编译器.函数的原型没有指定类型,因为这些函数具有变量类型.因此,实际数据类型不是在编译时确定的,而是在运行时确定的.然后该函数从堆栈中取一个参数,接着另一个参数.这些值没有与之关联的任何类型信息,因此唯一的方法是,函数知道如何解释数据,使用调用者提供的信息,即格式字符串.
函数本身不知道传入了哪些数据类型,也不知道传递的参数数量,因此无法printf自行决定.
在C++中,您可以使用运算符重载,但这是一种完全不同的机制.因为这里编译器根据数据类型和可用的重载函数选择适当的函数.
为了说明这一点,printf编译时如下所示:
push value1
...
push valueN
push format_string
call _printf
Run Code Online (Sandbox Code Playgroud)
原型printf是这样的:
int printf ( const char * format, ... );
Run Code Online (Sandbox Code Playgroud)
因此,除了格式字符串中提供的内容之外,不会传递任何类型信息.
mvp*_*mvp 13
编译器可能很聪明,但功能printf或scanf愚蠢 - 他们不知道你为每次调用传递的参数类型是什么.这就是你需要通过%s或%d每次都需要的原因.
当我声明我的变量时,编译器是否足够成熟以识别它?
没有.
您使用的是几十年前指定的语言.不要指望C语言的现代设计美学,因为它不是现代语言.现代语言倾向于在编译,解释或执行方面交换少量效率,以提高可用性或清晰度.C来自计算机处理时间昂贵且供应非常有限的时代,其设计反映了这一点.
这也是为什么当你真正关心快速,高效或接近金属时,C和C++仍然是首选语言的原因.