为什么每次都要在C中指定数据类型以打印F()和扫描F()?
从下面的代码片段中可以看到,我声明了一个为什么每次都要在C中指定数据类型以打印F()和扫描F()?,c,compiler-construction,printf,scanf,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)
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;
}
#包括
int main()
{
char-str[80];
int i;
printf(“输入您的姓氏:”);
scanf(“%s”,str);
printf(“输入您的年龄:”);
scanf(“%d”、&i);
返回0;
}
编译器可能很聪明,但函数printf
或scanf
很愚蠢-它们不知道每次调用传递的参数类型。这就是为什么每次都需要传递%s
或%d
。scanf
作为原型int scanf(const char*格式,…)代码>表示根据参数格式将给定数据存储到附加参数所指向的位置
它与编译器无关,所有这些都是为scanf
定义的语法。参数格式需要让scanf
知道为输入数据保留的大小。因为像scanf
和printf
这样的变量参数函数没有可移植的方法来知道变量参数的类型,甚至不知道传递了多少个参数
参见C常见问题解答:
这就是为什么必须至少有一个固定参数来确定变量参数的数量,可能还有类型。这个参数(标准称之为parmN
,参见C11()§7.16变量参数)起着这个特殊的作用,并将被传递给宏va_start
。换句话说,在标准C中,不能有这样的原型函数:
void foo(...);
printf
不是一个。它本身不是C语言的一部分。编译器所做的一切就是生成代码来调用printf
,传递任何参数。现在,由于C没有提供在运行时计算类型信息的机制,程序员必须显式地提供所需的信息。第一个参数是格式字符串。如果打印的是十进制数,则可能如下所示:
“%d”
(十进制数)
“%5d”
(十进制数字加空格,宽度为5)
“%05d”
(十进制数字以零填充至宽度5)
“%+d”
(十进制数字,始终带符号)
“值:%d\n”
(数字前/后的一些内容)
例如,请参阅了解字符串可以包含的格式
这里也可以有多个参数:
%s-%d”
(一个字符串,然后是一些内容,然后是一个数字)printf
和scanf
是以接收控制字符串和参数列表的方式设计和定义的I/O函数
函数不知道传递给它的参数的类型,编译器也无法将此信息传递给它。编译器无法提供必要信息的原因很简单,因为这里不涉及编译器。函数的原型没有指定类型,因为这些函数具有变量类型。因此,实际的数据类型不是在编译时确定的,而是在运行时确定的。
然后,该函数从堆栈中依次获取一个参数。这些值没有与之关联的任何类型信息,因此函数知道如何解释数据的唯一方法是使用调用者提供的信息,即格式字符串
函数本身不知道传入了哪些数据类型,也不知道传入的参数数量,因此printf
无法自行决定这一点
<>在C++中,可以使用运算符重载,但这是一个完全不同的机制。因为在这里,编译器根据数据类型和可用的重载函数选择适当的函数
为了说明这一点,编译时,printf
,如下所示:
push value1
...
push valueN
push format_string
call _printf
void foo(int a, int b, ...);
foo(1, 2, 3.0);
foo(1, 2, "abc");
my_exec("foo", "bar", "xyzzy", (char *) 0);
printf
的原型是:
int printf ( const char * format, ... );
因此,除了格式字符串中提供的信息外,没有任何类型信息
当我声明
变量
没有
您正在使用几十年前指定的语言。不要期望现代设计美学来自C语言,因为它不是一种现代语言。现代语言倾向于用少量的编译、解释或执行效率来换取可用性或清晰度的提高。C源于计算机处理时间昂贵且供应极为有限的时代,其设计反映了这一点
这也是为什么当你真的非常关心快速、高效或接近金属时,C和C++仍然是选择语言的原因。 < P> GCC(可能还有其他C编译器)至少在某些情况下跟踪参数类型。但语言不是这样设计的
printf
函数是一个接受变量参数的普通函数。变量参数需要某种运行时类型标识方案,但在C语言中,值不携带任何运行时类型信息。(当然,C程序员可以使用结构或位操作技巧创建运行时类型方案,但这些并没有集成到语言中。)
当我们开发这样的函数时:
push value1
...
push valueN
push format_string
call _printf
void foo(int a, int b, ...);
foo(1, 2, 3.0);
foo(1, 2, "abc");
my_exec("foo", "bar", "xyzzy", (char *) 0);
我们可以在第二个参数之后传递“任意”数量的附加参数,这取决于我们使用某种协议来确定有多少个参数以及它们的类型