为什么每次都要在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);
我们可以在第二个参数之后传递“任意”数量的附加参数,这取决于我们使用某种协议来确定有多少个参数以及它们的类型