输入函数scanf、getchar、gets等(C语言)

     

目录

     

缓冲区

scanf函数

getchar函数

        输入不确定数据

 gets函数

fgetc函数和getc函数

fscanf函数


 

缓冲区

  首先,让我们粗略的了解一下缓冲区的概念。

        缓冲区就是在内存空间中预留的一定的存储空间,这部分空间用来缓冲数据,也就是当输入或输出数据时(本章主要介绍输入),它会暂时不传过去,满足一定的条件再传。

        这样做的目的是提高计算机的运行速度,因为如果输入一个数据就让CPU处理,可能会打断CPU正在做的事情,使效率减慢。而缓冲区是在的数据取完后再一起到CPU中处理,这样就大大的提高了效率。

        缓冲区分为三类:全缓冲、行缓冲和不带缓冲。

        这三类与输入函数有关的是行缓存,因此本章仅介绍行缓存。

        行缓存:我们输入的字符先存放在缓冲区,等按下回车键换行('\n')时才进行实际后续操作。

        粗略了解这些后,我们开始对一些输入函数进行讲解。

scanf函数

首先就是我们最熟悉的scanf函数。

这是cplusplus中的定义

 

865d5f9df2ad4e7d99c7409a3e8706ad.png

        从stdin(这是一个输入流,本文不主要讲解)中读取格式化的数据(大致理解为从键盘读取数据就可以了)。

4d7f30c527b64e609ffa3c8f133f3cec.png

 

        从图中可以看出键盘的缓冲区就是行缓冲的类型。

        当然,scanf有着自己的规则,基础用法(粗略)如下:

	int a = 0;
	scanf("%d", &a);

         其中“%d”是指想要格式化输出的形式,因为我们在键盘上输入的其实是一串字符。

a36d32379b3a4de7bd140021695cb8c3.png

         这是指输入了‘1’、‘2’、‘3’、‘4’这四个字符,而我想要得到的是整数,于是“%d”格式化输出就将其转换为了整形也就是整数1234,存储在a所在的内存中,&a指的就是将获得的数据存入相应地址的内存处。

        对于scanf的返回值,我们从下面的测试来看。

b85f464a496442a9b6e8f9b46e06692a.png

输入数据

43e4d16517e146639d4184319624f59c.png

 得到

d52dcd290a5943108b7d235705861add.png

         可见,scanf的返回值就是它读取的数据个数。(后面还有相关介绍,需要用getchar(),所以先介绍下getchar)

getchar函数

这是cplusplus中的定义

ddf13797ba444bc38489ceffa19ca27e.png

        getchar的用法很简单,依旧是从键盘的缓存区读取数据。

getchar();

 5e0801a87c0245d7a732e173a699fcdc.png

         至于getchar的返回值,我们依旧来测试一遍。

14ccb391ee0d4bdebf5a3bdf035f5f44.png

输入任意字符

b284a2c9d8ef4bebb79b427697931f9c.png

 得到

8060f1b1b3d146d3be8a8ea934dc9952.png

         可见,getchar得到字符,返回该字符。这样我们可以用 getchar来查看缓存区还剩下什么。

注意:getchar的返回值为int类型,这里我们用char类型接收是为了方便看出输出的字符。而真正getchar返回的是读取字符的ASCII值。

如:

输入字符1 

d5326469e68a4f8db2ef85e604a3c2ce.png

 得到49

a0262b9a50294251a5c847bb0de66f3f.png

         而字符1的ASCII值正好就是49。(getchar返回值为正好也是它)(至于getcahr的返回值为什么为整形,可能与它遇到文件结尾会返回EOF,也就是-1有关,感兴趣的可以自己去了解)

那么了解了getchar之后,再让我们回到scanf函数。

        当我们使用scanf函数时,会发现,scanf函数一次只会读取一个数据。

        下面,我们输入 字符‘1’、‘2’、‘3’

e57ff0fceee4438fbc9a7a94758d8d40.png

b3d5565431a64da2b69e70d99db0a067.png

 

        可以看到,我们最终只读取了第一个字符‘1’ 

        另外,【1】scanf函数从缓冲区以字符的格式读的时候,输入的都可以读(包括空格(‘  ’)和‘\n’),【2】而当scanf以其他格式读取时,会自动跳过空格和换行符。

76b8d2a7a3174125bbd2be9a39a158eb.png

 以整形的格式读取,输入换行和空格

ba433a40c58647c3a3c470cd35c1be2c.png

4840894f112d46759f531d8f58d1afd9.png

        我们可以发现,程序一直没有往下进行,意思是scanf并没有读取到有效数据。 由此可知【2】

        而以字符的格式读取,输入换行和空格

我输入了空格和换行

84ec87f14b7345aa8169be3b327ab099.png

 直接看不出来,所以,我们转到监视去看a数组中存储的是否有空格和换行。

e24eb351af6041359449aa891e162d9c.png

         可以明显的看到a数组的前两个元素确实被改成了空格和换行,可得【1】

        因此,我们在连续输入整形等数据时,就可以在数据和数据之间加上空格,起到将字符串分割的作用,由此一次输入多组数据。

a6b66a2a3ed542c0a382252b819cf682.png

        这样,scanf的读取规则我们大致就了解了。

        输入不确定数据

        但是,如果我们需要的是输入一串不确定的数据又该怎么办呢? 

int main()
{
	int arr[100] = { 0 };
	int i = 0;
	do
	{
		scanf("%d", &arr[i]);
		i++;
	} while (1);
	return 0;
}

         假如我们用这种类似的循环结构,根据之前对scanf的了解,我们可以发现,这种代码会一直让你输入,因为'\n'不是停止的条件,那它也就不会停了。

        因此我们必须要加上一个停止的条件在while的判断中,这里我采用的是getchar,因为虽然scanf会跳过'\n'但是getchar不会。

#include<stdio.h>
int main()
{
	int arr[100] = { 0 };
	int i = 0;
	do
	{
		scanf("%d", &arr[i]);
		i++;
	} while (getchar()!='\n');
	return 0;
}

        这样一来,我们就可以输入一行任意个数的数据了,当然有的题目会要求输入几行,那么可以记住输入了几行,一次进行判断结束。(这样输入的话,必须要注意,最后一个数据的后面必须是‘\n’,不然getchar会读取其他字符而不会停止)

#include<stdio.h>
int main()
{
	int arr[100] = { 0 };
	int i = 0;
	int j = 3;
	do
	{
		scanf("%d", &arr[i]);
		i++;
		if (getchar() == '\n')
		{
			j--;
		}
	} while (j);
	return 0;
}

        这样就可以做到输入三行任意的数据了。

        我们都知道一个题目说反话,就是每个测试用例的输出占一行,输出倒序后的句子。

输入

hello world

输出

world hello

        这个题有一种解法为先分割字符串,再将串打印。

        那么,scanf和getchar组合能不能解决这个问题呢?

答案是肯定的

int main()
{
	char arr[10][10] = { 0 };//创建二维数组
	int k = 0;//设置变量,数组内容一次向后移
	do {
		scanf("%s", arr[k++]);//接收字符串,一个字符串存到一个数组中
	} while (getchar() != '\n');//判断输入是否结束
	while (k--) {
		printf("%s", arr[k]);//倒着依次打印字符串
		if (k != 0) {
			printf(" ");//最后一个不打印空格
		}
	}
	return 0;
}

        上面是创建二维数组接收一个个字符串,利用了scanf在以非字符的格式读取时,不读取'  '和'\n'。(注意:输入的最后一个数据后面只能是'\n',不然程序不能正常停止)(同时这种写法也有很大的弊端,那就是当我们想要连续输入两个空格时,程序不会读取,而是在打印时在两者之间增加一个空格,同时对于是因为想要输入一个空格但输入了两个空格的情况下和首位不小心输入了空格而言,这又是优势。当然,这段代码还可以改进,以适应各种特殊情况,这就靠大家的想象了,本文章就不过多扩展了)

        同时我们通过赋arr[0][4] = '#';arr[0][5] = '#';调试可发现

2dcd2a9e70ca413d8e207484d2c749bd.png

7ab5871d31dd45aa800c26a7aab3d57c.png

         arr[0][4] = '#'变成了 arr[0][4] = '\0';也就是说,scanf函数接收字符串时,也会像gets(后面有讲解)那样在字符串末尾加上字符串结束标志'\0'。

        当我们输入10个数据时,scanf会不会像gets函数那样报错呢?

        我们让   arr[1][0] = '#';arr[1][1] = '#';,方便观察

374fd882b18546808a52429e3187380d.png

 调出监视

2c53784fdc5e40a39fc28331341691f0.png

        可以看到因为是二位数组,即使arr[0]内存不够了,直接将最后的'\0'赋给arr[1][0],因此在用二维数组依次存字符串的时候,一定要开辟足够大的空间,不然会出问题,而且因为这不会报错,也很难找到错误。

        那么这还是把'\0'放进去了,我们试一下一维数组满了会怎样

a8f7d7a1454e4981bdd2b8aaa976063a.png

         结果超出了我的预料

eb3c58e5505544b49e60104554347811.png

         跟gets函数不一样的是,scanf函数将数据全都放进去了,程序也没有报错,这依旧提醒了我们,用数组存储字符串时一定要开辟足够大的空间。

gets函数

接下来是gets函数,一次读取一行数据。

这是cplusplus中的定义

0d59f2e6a7d442f29fffc5877384f031.png

        可以看出,gets函数是从键盘获取一行字符串,正常进行的话会返回此字符串的地址 ,char * str是你想存储的地址,一般是数组。

int main()
{
	char arr[100] = { 0 };
	gets(arr);
	return 0;
}

这样就将一串数据传了过去

27b6ec21ecca471187393a68783d3131.png

查看监视

b59409dfb4bf401d93daac581cb38893.png

 可以看到字符都传过去了,不仅如此,下面我们将arr[9]和arr[10]赋值为#,再次调监视。

69d8ff7bd9754a3eb08815e9c0689007.png

        我们可以看到arr[9]变成了'\0',也就是说gets函数获取字符时,自动在最后一位加上'\0',而scanf则没有这种行为。(由此也就让gets函数返回的地址在使用时,是一个标准的(字符结尾是'\0')字符串地址)

另外,结尾加'\0'的特性也就导致了放入数据时的内存问题。

int main()
{
	char arr[3] = { 0 };
	char*arr1=gets(arr);
	return 0;
}

该数组可存三个字符,如果我输入三个字符会怎样呢?它的'\0'会怎样处理呢?

07da0c74a4344fe1ae82b745f01f9f0e.png

         我们可以看到,程序直接报错了,因为数组越界了,也就是说,gets函数即使是数组满了,依然把'\0'加了上去,导致非法访问内存了,这函数还真倔。(另外,gets函数遇到文件结尾会返回NULL,一样,感兴趣的自己了解,本文章不讲这些)

          附加一些知识,gets读取了一串字符,从上面我们可以看到它并没有把'\n'读取到数组中,那么'\n'就依然留在缓冲区喽?很显然我这么问就是没有了,gets函数在读取完了之后,把'\n',“扔了”,也可以想成拿了它,知道该结束了,它也没啥用了,就扔了,总而言之,它就是不在了。

7e70e9a1e10b456da2582663c8da3cdb.png

b0f7174bfed4426da28ad61a4662a695.png

可以看到gets函数并没有读取'\n'

f62c3a0f6ef2428796a0858b45fc4803.png

当程序再往下进行时,需要我再次输入字符,说明缓存区的'\n'已经没了。

最后就剩下几个加f的函数了

34472d690dd740089d5d85f2b4bf69a3.png

 

3247957ff28f40e49fe20d737e918517.png

 

fgetc函数和getc函数

        这两个跟getchar很像,只不过getc和fgetc函数都是从stream(流)中获取数据,也就是说getc和fgetc获取数据的方式更多(如从文件中获取数据),不仅仅是从键盘获取数据。而getc和fgetc、getchar又有些小区别,前者是宏,后面两者是函数,getc一般情况下更快,但可能会有些宏具有的副作用。除了这些其他的基本都一样。

        以下是这两个函数从键盘上获取数据的方法(从stdin流中获取)

int main()
{
	getc(stdin);
	fgetc(stdin);
	return 0;
}

fscanf函数

0f5e88b9b91046429bcb89448fd590b1.png

         去对照scanf函数就可以发现,他们俩不一样在fscanf多了一个参数FILE * stream,看了这么多,你应该也知道了,fscanf也是获取数据的方式更多(如从文件中获取数据)。而FILE * stream就是数据获取的来源。

        以下是这该函数从键盘上获取数据(读)的方法(从stdin流中获取)

int main()
{
	int m = 0;
	fscanf(stdin, "%d", &m);
	return 0;
}

        最后就是fgets了fgets不仅仅与gets有流的范围的区别,他还能读取'\n'。

a78a900f857846cb81ac005acc41c7ae.png

int main()
{
	char arr[10] = { 0 };
	fgets(arr,40,stdin);
	return 0;
}

4f27dd9ab718417b9c634129e08f4062.png

f6a584dbd36945f6adfb66d954b5bcd8.png

可以明显的看到,gets函数将'\n'也拿了进去。 

二进制的fread与文件的关联性强,本文章就不具体介绍了。

        以上就是本人目前了解的输入函数。

 

 

 

 

 

 

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Soul&Spark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值