解决scanf的一些疑惑

一、前情提要:(引入几个概念)

1.空白字符:一般,程序中所指的空白字符是指空格(Space),回车(Enter)和制表符(Tab)。

2.缓冲区:

举个例子方便理解:

#include<stdio.h>
int main()
{
	char c;
	scanf("%c", &c);
	printf("%c", c);
	scanf("%c", &c);
	printf("%c", c);
	return 0;
}

如图,输入c加回车,还没输入第二个字符,程序就已经结束了
在这里插入图片描述
这是因为敲下的’\n’虽然看不见,但被放到了缓冲区。被第二个scanf读取,然后由第二个printf打印,这也是输出中第三行空着的原因。

为什么要有缓冲区呢?因为能提高效率。计算机读取磁盘的效率太低,先把磁盘数据放在缓冲区,计算机再直接从缓冲区中取数据,效率较高。

3.敲回车会换行(也就是敲一个回车会先换行,再在缓冲区存一个’\n’)

#include<stdio.h>
int main()
{
	char c;
	scanf("%c" ,& c);
	printf("%c", c);
	return 0;
}

在这里插入图片描述

输入和输出不在一行,不是连着的两个c。

4.scanf对不同类型输入的处理方式

(1)对于整型数据的输入,也就是"%d"类型的输入,scanf默认的分割符是所有的空白字符(空格,回车和制表符)。也就是说如果一个scanf函数中出现scanf(“%d%d”,&a,&b),那么用任何一个空白字符来分隔两个整数a,b的值,变量a,b都可以接收到正确的输入。另外,要注意的是,scanf对于数字输入,会忽略输入数据项前面的空白字符。

(2)scanf对于字符串输入(也就是"%s"类型的输入)的处理和对整数类似,会忽略前面的空白字符,而且默认的分隔符是所有的空白字符。但是,要注意的是,由于C语言中,没有string类型,都是用char型数组来表示。因此,scanf会为每一个输入的字符串最后加一个‘\0’。

(3)scanf在处理对字符数据的输入时,既不会忽略前面的空白字符,默认也没有任何分隔字符。所有的字符,包括空白字符都会被当成输入字符。

(4)scanf()函数接收输入数据时,遇空格、“回车”、Tab键结束一个数据的输入

(5)scanf()中%*表示忽略掉一个输入项。输入了啥,缓冲区中就少啥。(后面有举例)

二、解决上述缓冲区多一个’\n’的问题

目前我遇到有scanf的“%c”类型的输入和getchar()会遇到读取缓冲区残留的’\n’的情况。gets()中
换行符不作为读取串的内容,也就是遇到缓冲区的’\n’直接结束读取了,例如:

#include<stdio.h>
char str[100] = "\0";
int main()
{
	char c;
	scanf("%c", &c);
	printf("%c", c);
	gets(str);
	puts(str);
	return 0;
}

在这里插入图片描述
程序直接结束了,没有输入到str的机会,str中就只有一个’\0’。

至于解决scanf的“%c”类型的输入和getchar()会读取缓冲区残留的’\n’。以下有四种方法。

1.在想要赋值的语句前加上一句getchar()或者scanf(“%c”,&随便来个变量)

但这样遇到如下情况
在这里插入图片描述
输入aaaa加上两个空格再回车,缓冲区有两个空格和一个’\n’,所以需要三次来清空缓冲区。

2.所以可以用一个循环来清空缓冲区。
在这里插入图片描述
同样是输入aaaa加上两个空格回车,这次就能在后面输入一个值到变量c。(但缓冲区’\n’没了,所以只能在第二行输入)

3.当然可以用fflush函数来清除缓冲区。

4.只有一个’\n’的话,在%c前加一个空格或’\n’,那么缓冲区的’\n’就会被赋给%c前所加的空格或’\n’了。
在这里插入图片描述

三、scanf(“%d\n”,&a)回车无法结束输入问题

这种情况无论敲多少次回车键还是空格都结束不了。因为scanf直到发现非空白字符为止才可以再次用回车键结束输入。所以可以再打一个非空字符就结束了。
在这里插入图片描述

四、使用scanf输入包含空格的字符串

使用scanf(“%[^\n]”,str)来实现
%[]可以用来限制输入的内容,比如%[0-9]就表示只读入‘0’-‘9’之间的数字,%[a-z]表示只读入‘a’到‘z’之间的字母。我们这里的% [ ^\n ]中的 ^ 就表示“非”的意思,\n就是我们平时所说的回车符,所以%[^\n]这个符号就是表示只读取不是\n的字符。

但是多组输入的时候会死循环
在这里插入图片描述
要进行第二次输入时,从缓冲区读取到的第一个字符是上一次遗留下来的\n,scanf(“%[^\n]”,str)一遇到\n又马上停止了,什么都没读入。所以str数组中的内容没有被更新,依旧是上一次读取的内容。那该如何解决呢?

方法:使用getchar()去掉多余的回车符
在这里插入图片描述
输入回车又会发现新问题

(1)scanf(不正常)
在这里插入图片描述
(2)gets(正常)
在这里插入图片描述
gets的结果正常,如果直接输入一个回车,那也就输入一个回车,体现出来就是一行空行。而scanf中,如果直接输入一个回车,输出的内容居然是上一次输入(非回车)的内容。为什么会这样子呢?我们猜测,gets(str)的方式,如果读入的只有一个回车符,那么它会在str的第一个位置放入一个\0,所以输出的时候就啥都没有了;而scanf(“%[^\n]”,str)这个方式,如果读入的只有一个回车符,并不会对str做任何的改动。

所以严格一些来说,使用scanf(“%[^\n]”,str)配合getchar()这种方式进行多组输入时,必需确保在每一次输入前,字符串数组已被重新初始化。
改进版本如下:
在这里插入图片描述

五、C语言中的 %*s 和 %.*s

1.%*s

(1)对于printf,*表示用后面的形参替代的位置,实现动态格式输出

printf(“% * s”, 10, str);
意思是输出字符串str,至少占10个位置,str超过10个字符输出全部,不足10个的在字符串str左边补空格补到10个,这里等同于printf(“%10s”, str);这里的 * 被常量10代替,用于控制字符串宽度,主要是针对,最小字符宽度未知的情况,当然*可以对应整型变量

(2)对于scanf,表示忽略要读的项。比如 %*d 就是读一个 %d 该读的东西,但不赋值给任何变量。

在这里插入图片描述
​​​​​​​​​​​​​​%*s同理

在这里插入图片描述

2.%*.*s(第一个‘ * ’就是上述的‘ * ’)

对于printf,前面 * 定义输出总宽度,后面的 * 定义输出字符个数。

%.*s:如果所输出的字符串长度大于这个数,则按此宽度输出(图一),如果小于,则输出实际长度(图二的第三次循环)
在这里插入图片描述
在这里插入图片描述
%.*lf:可以控制输出的小数位数

在这里插入图片描述

六、printf(“%c”,‘\0’)

在这里插入图片描述
G没有被打印, 因为先执行print(++s),再是printf(“%c”, *s)。

其实打印的顺序是‘\0’,‘s’,‘u’,‘i’,‘e’,‘n’,‘e’。只是’\0’没有打印出来。
在这里插入图片描述
printf(“%c”,‘\0’);不会显示’\0’。

PS:

gets函数与scanf函数的区别

1.scanf(“%s”,&str) :读到空格便停止。
gets(str) :一直读到敲回车(不管中间是否有空格)。

2.gets函数一次只能输入一次字符串,scanf函数可输入多个
此外gets输入时自动将’\n’转换成’\0’,作为字符串结束的标志
要输入多个字符串,sacnf函数的输入表列中要用多个%s,不要加逗号等分隔符。例如scanf(“%s%s”,s1,s2);输入时,各字符串用空格分隔

puts函数与printf函数的区别

1.puts函数输出时会自动换行,printf函数则不会

2.puts函数一次只能输出一个字符串,printf函数可输出多个
puts输出时自动将’\0’转换成’\n’,并自动换行

printf(“%02x”, );

  • %x - 16进制打印形式
  • %2x - 要打印2个16进制位,不够2个位的时候使用空格填充
  • %02x - 要打印2个16进制位,不够2个位的时候使用0填充
  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在编写C语言程序时,如果使用了scanf函数,可能会出现C4996警告或错误。这个问题可以通过以下几种方法来解决。其中,方法一是指使用scanf_s函数替代scanf函数,这是一种在VS编译器下可以使用的解决方案。然而,需要注意的是,scanf_s函数只适用于VS编译器,在其他编译器下可能无法使用。因此,这种方法不能跨版本使用。 另一种解决方法是屏蔽C4996错误,可以在头文件之前添加以下一行代码#pragma warning(disable:4996),这样就可以禁用C4996警告。 当然,还有一种解决方法是使用_CRT_SECURE_NO_WARNINGS来禁用scanf函数的警告。可以在代码中添加以下一行代码:#define _CRT_SECURE_NO_WARNINGS。这样就可以消除关于scanf函数的警告信息。 需要注意的是,虽然这些方法可以解决C4996错误,但是使用scanf函数时需要小心,因为它可能存在安全问题。在某些情况下,更推荐使用scanf_s函数来替代scanf函数,以确保代码的安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [c语言——程序出现C4996:scanf 等错误的解决方法](https://blog.csdn.net/lxl700204/article/details/104859819)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [VS中报错C4996 scanf错误&错误介绍&解决方案](https://blog.csdn.net/m0_61146840/article/details/124521380)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值