为什么每次在C中都必须指定数据类型?

Akk*_*619 66 c compiler-construction printf scanf

从下面的代码片段中可以看出,我已经声明了一个char变量和一个int变量.当代码被编译,就必须确定变量的数据类型stri.

为什么我需要扫描我的变量,它是通过指定的字符串或整型变量时再次告诉%s%dscanf?当我声明我的变量时,编译器是否足够成熟以确定?

#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)

  • 这里唯一的答案是+1,它可以识别问题所在......缺少varargs的静态类型. (21认同)
  • 另外,如上面的评论中所述,var类型和`printf`格式"类型"之间没有一对一的对应关系. (3认同)

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)

因此,除了格式字符串中提供的内容之外,不会传递任何类型信息.

  • @ Max-P,是的,这绝对是一个优势,但也是一个危险的垃圾填埋场.:)当然,无论如何你都必须编写一些包装器,因为如果你可以在命令行上提供格式字符串(可以这么说),你必须确保也提供了适当的参数. (3认同)

Tar*_*rik 14

printf不是内在的功能.它不是C语言本身的一部分.所有编译器都会生成要调用的代码printf,传递任何参数.现在,因为C不提供反射作为在运行时计算类型信息的机制,所以程序员必须明确地提供所需的信息.


mvp*_*mvp 13

编译器可能很聪明,但功能printfscanf愚蠢 - 他们不知道你为每次调用传递的参数类型是什么.这就是你需要通过%s%d每次都需要的原因.

  • 在C++中,cin/cout的例子是不合适的.这样做的原因是因为重载,所以编译器可以在编译时决定它将为调用提供哪个函数.这是一个完全不同的机制,然后varargs.对于用户来说,它看起来很相似,但从技术上讲并非如此. (8认同)
  • 那是因为C++支持运算符重载,这最终意味着你有一个<< string的方法,另一个用于<< int等等...在像C这样的过程语言中,它等同于具有print_str,print_int ...的函数. (7认同)

Kos*_*Kos 10

第一个参数是格式字符串.如果您要打印十进制数字,它可能如下所示:

  • "%d" (十进制数)
  • "%5d" (十进制数用空格填充到宽度5)
  • "%05d" (用零填充宽度为5的十进制数)
  • "%+d" (十进制数,总是带符号)
  • "Value: %d\n" (数字之前/之后的一些内容)

等,请参阅维基百科上的格式占位符,以了解字符串可以包含的格式.

此处还可以有多个参数:

"%s - %d" (一个字符串,然后一些内容,然后一个数字)


Jac*_*ley 8

当我声明我的变量时,编译器是否足够成熟以识别它?

没有.

您使用的是几十年前指定的语言.不要指望C语言的现代设计美学,因为它不是现代语言.现代语言倾向于在编译,解释或执行方面交换少量效率,以提高可用性或清晰度.C来自计算机处理时间昂贵且供应非常有限的时代,其设计反映了这一点.

这也是为什么当你真正关心快速,高效或接近金属时,C和C++仍然是首选语言的原因.