目录
频道首页
C中scanf函数的用法和类型转换详解_scanf的数据类型_无梦
收藏
0
The_Byte_Station 最近修改于 2023-10-29 11:57:28

scanf函数的作用是什么

  1. scanf函数是C语言库中多个输入函数中最通用的一个,因为它可以读取不同格式的数据。
  2. 从键盘中输入的都是文本,即键盘只能输入字母,数字和标点符号,如果要输入整数2023,需要依次键入2、0、2、3。但对于电脑来说,从键盘上键入的任何值都为字符,要想将其存储为数值而非字符串,程序就必须把字符依次转换成数值,这就是scanf函数要做的事情。
  3. scanf函数可以将输入的文本转换成整数、浮点数、字符和字符串。这里我们可以跟printf函数相比,prinf函数正好与scanf函数相反,prinf函数是把整数、浮点数、字符和字符串转换成文本。
  4. 什么是缓冲区缓存区是计算机内存中的一部分,用于临时存储数据,以加快数据读写速度。缓存区通常位于CPU和其他设备(如硬盘、网络接口卡等)之间,充当缓冲区的角色。例如,当从硬盘上读取大量数据时,读取时间往往很长,因为硬盘的读取速度较慢。如果在缓存区中存储一部分数据,那么访问稍后需要的数据可从缓存区中读取,从而大大加快了数据访问速度。同样,当将数据写入硬盘时,系统会将数据先缓存到缓存区中,然后在合适的时间将数据写入硬盘,这样可以避免频繁地写入硬盘时由于磁盘的旋转速度限制而造成的性能瓶颈。因此,缓存区是一种临时存储数据的方式,以提高计算机系统的性能和效率。它不能存储大量数据,但它具有高速读取和写入的能力,使数据的访问速度大大提高,而scanf函数就是在缓冲区读取数据的。

1. scanf的用法

1. 案例

我们先来看看以下代码:

#define  _CRT_SECURE_NO_WARNINGS    
//该语句是为解决vs编译器中关于scanf函数不安全而报错
#include  
int main()
{
   int a=0;
    scanf("%d",&a);    //括号中"%d"即为格式字符串  逗号之后的内容即为参数列表
    printf("%d",a);    //我们可以和scanf函数相比一下,发现两者非常相似
    return 0;
}

2. 代码分析:

我们来分析以上代码:

  1. scanf函数和prinf函数类似,也使用格式字符串和参数列表。scanf函数中的格式字符串表明字符输入流的对应类型。
  2. 参数列表中,prinf函数使用变量,常量和表达式 而scanf 函数使用指向变量的指针,这里,如果读者如果还未了解如何使用指针,可以记住以下两条简单的规则:如果scanf函数读取基本变量类型值时,在变量名前加上一个&;如果scanf函数读取字符串时,不需要&。

2. 解决vs的scanf函数报错

关于#define CRTSECURENOWARNINGS , scanf函数在VS中报错的主要原因是 scanf被认为不安全而被编译器默认设置为禁用。

法一:

我们可以最顶部使用#define CRTSECURENOWARNINGS 这个语句来去除vs的警告,注意:一定要是最顶端,否则还是会报错。但缺点是每次编写程序使用scanf函数时都重复此操作,非常麻烦。

法二:

我们也可以使用scanf_s这个vs编译器内置的函数(非c语言的标准库内的函数)来实现输入。但因为该函数并非C标准库内的函数,使用时并不方便移植代码,所以并不推荐该方法。

法三:

接下来,我们讲一个可以一劳永逸的方法。打开文件资源管理器在这里插入图片描述

这样就完成了,之后我们使用vs创建C/C++的源文件时,会自动在开头加上#define CRTSECURENOWARNINGS该语句来去除vs编译器中的警告。

代码案例:

#define  _CRT_SECURE_NO_WARNINGS    
//该语句是为解决vs编译器中关于scanf函数不安全而报错

#include  
int main()
{
   int a=0;              //整型变量
   float b=2.5;            //浮点型变量
   char arr[30];        //字符数组,用于存储字符串
   scanf("%d %f %s",&a,&b,arr);    //arr是字符串,跟前两者相比不需要使用&
   printf("%d %f %s\n",a,b,arr);   //打印结果
    return 0;
}

3. scanf函数的重点和易错点

1. 输入注意项

scanf函数是使用空白(换行符、制表符和空格)来把输入分成多个字段(参数),输入项在同为数字字符或非数字字符的情况下,需要在每个输入项之间输入至少一个空白即来实现多项输入。在依次把转换说明和字符匹配后,通过空白来结束当前匹配,若有多个字段(参数),则进行下个转换说明的匹配。

#define  _CRT_SECURE_NO_WARNINGS    
//该语句是为解决vs编译器中关于scanf函数不安全而报错
#include  

int main()
{
   int a=0;             //整型变量
   int b=0;             //整型变量
   int c=0;            //整型变量
   scanf("%d %d %d",&a,&b,&c);    
   printf("a=%d b=%d c=%d\n",a,b,c);   //打印结果

   int d=0;
   float e=2.5;
   char arr[30];
   scanf("%d %f %s",&d,&e,arr);  //把这里的控制台输入与上面的情况相比
   printf("%d %f %s\n",d,e,arr);   //打印结果
   return 0;
}

在这里插入图片描述

可以看到如下情况,当输入项之间为字符和非字符时,中间的空白可以省略。在这里插入图片描述

2.转换说明表和修饰符表

在这里插入图片描述

scanf函数所使用的转换说明与printf函数几乎相同,主要的区别是,对于float类型和double类型,printf使用%f、%e、%E、%g和%G来转换说明。而scanf只能把它们用于float类型,对于double类型,要使用 l 修饰符。即%ld来转换说明。

3.格式字符串中的普通字符

scanf函数允许把普通字符放在字符串中。但是,除了空格字符外的普通字符都必须与输入字符串匹配。在平时编写程序时并不推荐这么做。例如,

scanf("%d,%d",&n,&m);

scanf函数会将代码解释为用户将输入一个数字,一个逗号,然后再输入一个数字。也就是说,必须这样输入88,111在scanf函数的格式字符串中的空白意味着跳过下一个输入项前面的所有空白。例如,

scanf("%d ,%d",&n,&m);

以下输入格式都没问题88,12188 ,12188,121注意,所有空白包括没有空白这一特殊情况。当然,在scanf函数中,除了%c转换说明,scanf按其他的转换说明在缓冲区读取数据时,都会跳过读取数据前面的所有空白。

案例1:

#include  

int main()
{
   int a=0;             //整型变量
   int b=0;             //整型变量
   int c=0;            //整型变量
   scanf("%d%d%d",&a,&b,&c);    //格式字符串中间并没有空白
   printf("a=%d b=%d c=%d\n",a,b,c);   //打印结果

   return 0;
}

结果:在这里插入图片描述

可以看到,在4和5前都有很多空格,输入完5之后,我直接换行输入8,结果scanf函数也能正常读取。

案例2:

#include  


int main()
{
   char a='1';             //字符变量
   char b='1';             //字符变量
   char c='1';             //字符变量
   scanf("%c%c%c",&a,&b,&c);  
   printf("a=%c b=%c c=%c\n",a,b,c);   //打印结果

   return 0;
}

结果:在这里插入图片描述

要想使结果为输入值:可以使用以上刚讲到的在格式字符串中添加空白。

#include

int main()
{
   char a='1';             //字符变量
   char b='1';             //字符变量
   char c='1';             //字符变量
   scanf("%c %c %c",&a,&b,&c);  
   printf("a=%c b=%c c=%c\n",a,b,c);   //打印结果
   return 0;
}

结果:在这里插入图片描述

4.从scanf函数角度看输入

接下来,我们来详细了解一下scanf函数是这么读取输入的。

1.什么是字段宽度

在这里插入图片描述

#include  

int main()
{
   char arr[10];
   scanf("%3s",arr);     //输入字符串
   printf("arr=%s\n",arr);   //打印结果
   return 0;
}

结果为:在这里插入图片描述

可以看到,arr数组明显是容得下hello的,但scanf函数读取到hel就停止了,这显而易见是字段宽度的原因。

这里要注意的一点是,scanf读取完字符串后,会在字符串后自动加上一个’\0’。所以,在存储数据时,我们要准备比输入的字符串个数多一个存储空间来存储字符串。

2.详解scanf函数输入

假设scanf根据一个%d转换说明读取一个整数。除%c需要在格式字符串中添加空白来说明跳过空白外,其他转换说明都会自动跳过读取数据前的所有空白。

所以,scanf会跳过缓冲区中要读取数据前的所有空白字符,直至遇到第一个非空白字符才开始读取。因为要读取整数,所以scanf希望发现一个数字字符或者一个符号(+或-)。

如果遇到一个数字或正负符号,它便读取和保存该字符,然后继续读取下一个字符,直到遇到非数字字符或(如若使用字符宽度)达到字符段宽度,它便认为读到了整数的末尾,如若读取到非数字字符,会将读取到的非数字字符放回缓冲区,然后计算已读取字符(可能还有符号)相对应的值,并将计算的结果保存到与该转换说明对应的参数变量中。

若格式字符串还有转换说明符,则scanf函数会继续读取,要注意的是,继续读取的内容为上一次读取丢弃的内容,若下一个转换说明符还是%d,scanf将停在这里,并如同上次读取一样把非数字字符放回缓冲区,不会把值赋给指定变量。这时候,scanf函数就无法越过非数字字符读取下一个字符。程序会出现bug。

要注意的是,如果使用带多个转换说明的scanf函数,C规定程序会在第一个出错处停止读取输入。

当然,用其他转换说明读取输入和用%d情况相同。区别在于scanf函数会识别更多的字符,例如,%x要求scanf函数识别十六进制数af和AF,浮点数会要求scanf函数识别小数点、e计数法(指数表示法)和新增的p计数法(十六进制指数表示法)。

如果使用%s转换说明符,scanf会读取所有的非空白字符,跳过空白读取第一个非空白字符,直到遇到第一个空白字符或达到字符宽度,才停止读取。这意味着使用scanf读取的字符串是不会有空格的。(可以使用getchar函数或fgets()函数来读取空格)读取完后,scanf会自动在字符串最后加上’\0’。

4. 类型转换

C语言中的类型转换分为显性转换和隐性转换。这里我们先来讲隐性转换。

1. 隐性转换

隐性转换分为算术转换,赋值转换和输出转换。

在讲算术转换之前,我们先来讲一下一些特殊的转换。

  1. 在计算过程中,不管是否有混合类型 short 和 char 类型都会先自动转换成 unsigned int 或 signed int (int)。 (如果short与int的大小相同, unsigned short 就比 int 大。这种情况下, unsigned short会被转换成unsigned int)。在K&R那时的C中, float会被自动转换成double (目前的C不是这样)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级(promotion)。划线标蓝部分来自《C primer plus》这本经典著作。
  2. 当 char 和 short 类型作为参数传递时. 会被转换 int 类型,float 类型会被转换成 double 类型。
1.1 算术转换

在语句和表达式中使用混合类型,C会采用一套规则进行自动类型转换,即为算术转换。

即表达式或语句中的低类型会向高类型转换。当然,并非直接将所有低类型转换成最高类型,而是按照优先级和结合性逐步转换。

类型级别从高到低依次是:long doule,doule, float,usigned long long , long long , usigned long , long, usigned int , int 。之所以没有出现short 和 char,是因为它们已经自动转换成 int 或 unsigned int 了。

例如:

char ch='5';  //字符类型在内存存储的是ascll码值,'5'的ascll码值为53
int i=3;
float fl=2.5;
float f2;
f2=f1+i*ch;

在第5行中,表达式会先计算ich,根据上面讲到的规则,ch会先转换成比它更高级的int类型,再计算ich(3*53);为了和f1相加,乘积整数(159)会被转换成浮点类型,最后计算结果被存储在f2中。

1.2 赋值转换

在赋值表达式语句中,计算结果会被转换成赋值变量的类型。这个过程可能发生升级或降级(截断)。所谓降级,即高类型向低类型转换,当然,降级可能导致一些麻烦。

下面,我们来讲解一下对应规则和一些案例。

  1. 目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。例如,如果目标类型是8位unsigned char ,待赋的值是原始值求模256。
  2. 如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
  3. 如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的。如果把一个浮点值转换成整数类型会怎样?当浮点类型被降级为整数类型时,原来的浮点值会被截断。例如,23.12和23.99都会被截断为23,-23.5会被截断为-23。划线标蓝部分来自《C primer plus》这本经典著作。

案例:

char ch='C';
int a = 0;
ch = 1107;
a = 80.98;

第2行将超出ch类型范围的值赋给ch,会出现降级行为,最终ch值为字符S(83)。(ch%256=83)第3行把浮点数赋值给a,则会发生截断,a最终的结果为字符P(80)。

1.3 输出转换

在程序中将数据用printf函数以指定格式输出时,当要输出的盐据类型与输出格式不符时,便自动进行类型转换,如一个long型数据用整型格式(%d)输出时,则相当于将long型转换成整型(int)数据输出;一个字符(char)型数据用整型格式输出时,相当于将char型转换成int型输出。链接注意:高类型转换成低类型输出时,其值不能超出短型数据允许的值范围,否则转换时将与预期结果不符合。即对应结果会发生(降级)截断行为,而低类型转换成高类型,也会发生升级行为。如:

char a = '0',   //字符0的ascll码值为48;
printf("%d",a);   //结果会将字符0转换成整型输出

long b = 50000;
printf("%d",b);

运行结果为17232,因为int型允许的最大值为32767,50000超出此值,故结果取以32768为模的余数,即进行如下取余运算:50000%32768 =17232;

当然,这并不意味着可以随意编写代码来使用printf函数。printf函数并不支持处理输出格式和输出的数据不符的情况。若输出的数据类型与输出格式不符时,也发生错误,如:

int a=5;
printf("%f",a);

float b=1.5;
printf("%d",b);

这样编写代码将产生错误的结果。

2. 显性转换

通常,我们应该避免自动类型转换,特别是类型降级。但有时,我们需要更加精确的转换,这时候就需要用的强制类型转换,即显性转换。其通用形式为:(type)

案例:

float m = 2.5;
float n = 1.7;
int t = 0;
t = m + n;   //t=4

// 若我们希望结果是m和n两者都忽略小数相加
t=(int)m+(int)n;    //t=3;

第4行使用自动转换类型,m+n结果为4.2,再将结果降级赋给t,t最终的结果为4;第7行使用强制类型转换,先将m和n都转换为整型,即2和1,再将两者相加的结果赋值给t,t最终的结果为3;一般情况下,不应该混合使用类型(因为有些语言并不允许这样做),但对于C语言来说,偶尔这样做还是有用的。

内容大纲
批注笔记
C中scanf函数的用法和类型转换详解_scanf的数据类型_无梦
ArticleBot
z
z
z
z
申请加入
主页
会议室
Git管理
文章
云文档