scanf()函数分析

首先,先来讲一下scanf的读取流程:

从键盘输入的都是字符类型(一系列的字符),scanf()的作用就是将这个字符序列转换成一个或多个指定的类型,并保存到变量中。

从键盘输入的字符序列会先缓存到键盘缓冲区中,当用户输入回车,这时会清空键盘缓冲区,将键盘缓冲区的数据(包括回车)送入到stdin中

这时scanf()开始从stdin中读取数据。

scanf()在读取每个字段时都会忽略空白符(%c比较特殊)。以%d为例,scanf()会先忽略stdin中的空白符,直到遇到第一个0-9开始读取,如果后面的字符依然是0-9就继续读取,直到遇到空白符或者非0-9的字符,scanf认为%d的读取完毕,将读取的字符序列转换成十进制整型保存到变量中。最后遇到的空白符或非法字符将返回到stdin中去。

如果忽略掉前面的空白符后第一个遇到的是非法的字符(非0-9),比如a,这时a会被返回到stdin中,程序也将会跳出scanf()函数(不管%d后面是否还存在带输入项,如另一个%d,都会跳出整个scanf()函数)。

看一个例子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c[10];

    printf("请输入:\n");
    scanf("%d%d",&a,&b);
    scanf("%s",c);
    printf("a=%d,b=%d,c=%s\n",a,b,c);
    return 0;
}

输入和输出结果为:

可以看到我在相邻的两个待输入字段之间添加了多个空白符,但是这并不会影响输入(当有%c的时候会有影响),这就是因为scanf()在读取每个待输入字段前都会跳过前面的空白符。

同样的程序,在看一组输入和输出结果。

这个我先输入了一个对于%d是非法的字符s,第一个scanf()读取的时候会先跳过s前面的空白符,然后遇到s,发现s与%d不能匹配,这时候就会将s返回到stdin中,并跳过第一个scanf(),所以我们看到a和b的值都没有被改变,然后开始执行第二个scanf(),这个时候遇到的第一个字符是s(之前scanf返回到stdin中),然后接着读取,1、2、3、4知道遇到空白符,认为%s读取结束,将读入的字符序列转换称一个字符串(添加上‘\0‘),然后保存到c所指的地址中。

再来看一个格式字符串中含有普通字符的例子。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c[10];

    printf("请输入:\n");
    scanf("%d,%d",&a,&b);
    scanf("%s",c);
    printf("a=%d,b=%d,c=%s\n",a,b,c);
    return 0;
}

与上一个例子不同的是第一个scanf()的两个%d之间多个一个",",这时在输入的时候要特别注意也要输入这个逗号,而且这个逗号相对于前一个%d的位置也要准确,否则scanf()就会读取失败,然后跳出这个scanf().

看一个正确的输入:

如果我们输入的时候95后面跟的不是逗号(全半角错误也不行),scanf就会读取失败,然后将该字符返回到stdin中,然后跳过这个scanf。

看一个错误的输入

这次输入时95后面的是中文形式的逗号,与代码中英文形式的逗号不匹配,这个时候就会出错,然后中文的逗号就会被返回到stdin中,并跳出第一个scanf,开始第二个scanf(),所以b的值没有读取进去。而c的值中也含有第一个scanf()返回到stdin中的逗号。

再看一中错误的输入:

这次在95后面添加了几个空白符,然后才是逗号。前面强调scanf在读取每个待输入的字段的时候都会跳过空白符,但是要明白,对于第一个scanf(),它的待输入项就是两个%d,逗号并不是它的待输入项,所以逗号前面的空白符不会被跳过,所以出现了和代码中的逗号不匹配的情况(实际的输入是空格),所以也会像前一种情况一样读取错误,并跳过第一个scanf。

再看一种正确的输入:

这个输入中我们在逗号后面添加了多个空格,发现输入时正确的。这是因为这些空格位于第二个%d的前面,也就是说位于第二个待输入项的前面,这时scanf就会跳过这些空白符。

再看一种代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c[10];

    printf("请输入:\n");
    scanf("%d\n,%d",&a,&b);
    scanf("%s",c);
    printf("a=%d,b=%d,c=%s\n",a,b,c);
    return 0;
}

这次我们在第一个%d和逗号之间添加了一个\n(其实只要是空白符即可)。

输入输出结果

这次我们发现在95和逗号之间添加了多个空白符也正确,这是因为添加的\n会让scanf()跳过%d和逗号之间的空白符。将\n换成空格也是正确的(只要是空白符就行)

比如换成空格:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c[10];

    printf("请输入:\n");
    scanf("%d ,%d",&a,&b);
    scanf("%s",c);
    printf("a=%d,b=%d,c=%s\n",a,b,c);
    return 0;
}

最后看一种含有%c的情况

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c;

    printf("请输入:\n");
    scanf("%d,%d",&a,&b);
    scanf("%c",&c);
    printf("a=%d,b=%d,c=%c\n",a,b,c);
    return 0;
}

我们输入95,21回车,scanf的读取流程是这样的:

首先遇到95然后遇到逗号,发现逗号和%d不匹配,就将95转换成整型保存到a中,并将逗号返回到stdin中,然后继续读取,读取到逗号(刚刚返回到stdin中的)发现和代码中的逗号匹配,所以会跳过这个逗号,然后读取到21,然后是回车,发现空白符,则认为第二个%d读取结束,所以讲21转换成整型保存到b中,并返回空白符回车到stdin中,接着开始进行第二个scanf函数,其中%c可以读取任何字符(通吃啦),所以空白符对于%c来说是合法的,回车自然也是合法的,所以它就将回车保存到了c中。所以我们看到输出结果中c的内容为空,但是Press any key to continue前面有一行空行,这实际上就是读入到c中的换行符。

所以如果我们输入的是95,21空格(多个)x回车,那么空格就将被读入到c中,除了第一个空格,其余的字符还在stdin中:

那么怎么解决这种问题呢?  因为毕竟我们想让c读取x,而并不是空格。

一种可能的解决方法是在第一个scanf()的最后一个%d后面加入一个空白符(空格、\n、\t都是可以的),以吸收21和x之间的空白符。(我才用的是\n)

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a=123;
    int b=11;
    char c;

    printf("请输入:\n");
    scanf("%d,%d\n",&a,&b);
    scanf("%c",&c);
    printf("a=%d,b=%d,c=%c\n",a,b,c);
    return 0;
}

结果是正确   ^^

先写到这里啦,有问题再补充。。

如果你觉得对你有一丝的帮助,请点赞哦  ^^

补充:当scanf中使用数字修饰限制字段的长度时,遇到空白符(或非法字符)或者达到字段长度(二者满足一个即可)就意味着这个字段读取完成了

如果你觉得对你有一丝的帮助,请点赞哦  ^^

时间: 2024-08-28 21:29:49

scanf()函数分析的相关文章

ios-(5)变量的内存分析、Scanf函数

一.变量的内存分析 1.字节和地址 * 为了更好地理解变量在内存中得存储细节,先来认识一下内存中得"字节"和"地址". * 内存以字节为单位 * 不同类型占用的字节是不一样的,数据越大,所需的字节数九越多 2.变量存储 * 所占用的字节数跟类型有关,也跟编译器环境有关   16位编译器 32位编译器 64位编译器 char 1 1 1 int 2 4 4 float 4 4 4 double 8 8 8 * 变量实例 int b =10; int a = 20; #

黑马程序员——C语言变量内存分析与scanf函数

Java培训.Android培训.iOS培训..Net培训.期待与您交流! 1.变量的内存分析 1)变量以字节为单位,每个字节都有自己的内存地址,根据地址就可以找到该字节.整个内存相当于一整个酒店,而酒店以房间为单位,在这里每个房间就相当于是每个字节,地址就是房号,根据房号可以找到房间(根据地址也可以找到内存中的字节).相邻房间的房号是连续的,相邻字节的地址也是连续的.计算机中通常以十六进制表示地址.变量所占用字节数跟类型有关,也跟编译器环境有关 2)内存由大到小寻址,优先分配内存地址较大的字节

C语言Scanf函数

C语言的scanf函数 一.变量的内存分析 (一)字节与地址 ①. 内 存以字节为单位 每个字节都有自己的内存地址,根据地址就可以找到该字节.整个内存相当于一整个酒店,而酒店以房间为单位,在这里每个房间就相当于是每个字节,地址就是房 号,根据房号可以找到房间(根据地址也可以找到内存中的字节).相邻房间的房号是连续的,相邻字节的地址也是连续的.计算机中通常以十六进制表示地址. ②. 不同类型占用的字节是不一样的,数据越大则占用的字节数越多.如在64位编译器下,int类型占据4个字节,char类型占

黑马程序员————C语言基本语法(关键字、标识符、注释符、变量、Scanf函数)

第一讲 关键字 1.什么是关键字? 1) 关键字就是C语言提供的有特殊含义的符号,也叫做“保留字” 2) C语言一共提供了32个关键字,这些关键字都被C语言赋予了特殊含义 C语言关键字 auto局部变量(自动储存) break 无条件退出程序最内层循环 case   switch 语句中选择项 char  单字节整型数据 const  定义不可更改的常量值 continue 中断本次循环,并转向下一次循环 default switch 语句中的默认选择项 do  用于构成do.....while

05-C语言scanf函数

一. 变量的内存分析 1. 字节和地址 为了更好地理解变量在内存中的存储细节,先来认识一下内存中的"字节"和"地址". 1> 内存以"字节为单位" 0x表示的是十六进制,不用过于纠结,能看懂这些数字之间谁大谁小就行了 2> 不同类型占用的字节是不一样的,数据越大,所需的字节数就越多 2. 变量的存储 1> 所占用字节数跟类型有关,也跟编译器环境有关 2> 变量实例 int b = 10; int a = 20; l 内存由

C语言之可变参实现scanf函数

既然有printf函数可变参实现,那就一定有scanf函数的可变参实现.废话不多说,源码奉上: 本源码不过多分析,如要明白原理,请翻本博客以往的文章看说明. 欢迎关注新浪微博:http://weibo.com/u/1896293701/home?topnav=1&wvr=6 #include <stdio.h> #include <stdarg.h> int myscanf(const char *fmt , ...) ; int main(void) { int num

【黑马程序员】————scanf函数

变量的内存分析 1.内存寻址由大到小,优先分配内存地址比较大的字节给变量 2.变量越先定义,内存地址就越大 3.取得变量的地址:&变量名 4.输出地址:%p 5.一个变量一定先进行初始化,才能使用 查看内存地址: int a;printf("a的地址是:%p\n", &a); scanf函数 1.      简介 这也是在stdio.h中声明的一个函数,因此使用前必须加入#include <stdio.h>.调用scanf函数时,需要传入变量的地址作为参数,

04-常量、变量、scanf函数和sizeof

一.常量 1. 什么是常量 常量,表示一些固定的数据 2. 整型常量(int)和浮点型常量(float/double) 浮点型常量分为double和float两种数据类型 * double:双精度浮点型,其实就是小数.比如 5.43.-2.3.0.0等 * float:单精度浮点型,也是小数,比double的精确程度低,也就是说所能表示的小数位数比较少.为了跟double区分开来,float型数据都是以f结尾的,比如5.43f.-2.3f.0.0f. 3. 字符常量 * 将一个数字(0-9).英

黑马程序员--C语言基础之--scanf函数

------- IOS培训期待与您交流! ---------- 写代码的时候,我们可以看得出,几乎每一个C程序都会包括输入输出.输入输出是程序中最基本的操作之一.C语言本身并不提供输入输出的语句,输入输出操作是由C标准函数库中的函数来实现的.今天就先说说输入:scanf函数. scanf函数与printf函数一样,都不是C语言的关键字,而只是函数库的名字,它们被定义在stdio.h里,因此在使用scanf函数时要加 上#include<stdio.h>.它是格式输入函数,即按用户指定的格式从键