gets从标准输入设备读字符串函数,其可以无限读取,不会判断上限,以回车结束读取,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。
#include <stdio.h>
int main(void)
{
int a;
char b[40];
scanf("%d",a);
gets(b);
printf("%s",b);
return 0;
}
运行结果为:
输入1后,程序便结束,说明gets()已经将换行符读入了,那么实践证明能够这样,关键是原理是如何,即缓冲区的作用原理是什么?
而且,我修改了一下代码:
#include <stdio.h>
int main(void)
{
char a[40];
char b[40];
gets(a);
gets(b);
printf("%c",b[2]);
return 0;
}
运行结果为:
修改之后,第二个gets便不会读入换行符,这又是为什么?
先对第一个代码的解释与疑问:
第一段已经说明了,gets函数是会把换行符读入然后舍弃并换成'\0'这个字符,我们可以试验:
#include <stdio.h>
int main(void)
{
int a;
char b[40];
scanf("%d",a);
gets(b);
printf("%d",b[0]);
return 0;
}
运行结果:
可以看出,字符串数组b的第一个元素的ASC2码的值便是0,即'\0'的ASC2码值。
那么这样就更能解释第二个代码了,第一个gets已经将换行符读入了,所以第二个gets就读不到第一串字符的换行符了。
①但是这样我就更加对缓冲区有疑问了,它的作用原理到底是什么?
按下回车后,到底什么会留在缓冲区,什么会被刷新呢?
现在我对这个问题还不是很理解,希望大家能指点一二。
ps:摘自百度:gets()函数读取到\n(我们输入的回车)于是停止读取,但是它不会把\n包含到字符串里面去。然而,和它配合使用的puts函数,却在输出字符串的时候自动换行。
2.
代码:
#include <stdio.h>
int main(void)
{
char a[3] ;
int b;
scanf("%d",&b);
scanf("%s",a);
printf("%d",b);
return 0;
}
从中
1.用scanf("%s",a)不会读入换行符(再输入b以后,应该还有一个换行符,如果是gets,应该会吃掉这个换行符)
2.数组越界后,输出还是和输入的一样(查过一些资料,有时候可能会出错)
这里猜测,第二点是因为,数组越界后,以后的数据可能会保存到这段内存之外的内存区,(有时候出错可能是因为内存之外的内存区被系统禁用),但是我无法具体解释b的值。
在这里便区别一下scanf(“%s”)和gets:
摘自百度:
1、 gets功能为读入一行,并将换行符转换为字符串结束符。 2、 scanf("%s",s);读入时,遇到空白字符,包括空格,制表符,换行符时均会停止输入。 从功能上可以看出不同之处: 1 终止条件不同。gets只有遇到\n时才会结束输入,而scanf遇到空格或制表符时,也会结束输入。 比如输入"test string\n"。 用gets得到的字符串为"test string", 二用scanf得到的是"test"。 2 终止后,对终止字符处理不同。 比如输入为"test\nabcd"。 执行gets后,\n不会留在缓冲区中,即这时调用getchar得到的字符是'a'。 执行scanf后,\n会留在缓冲区,这时调用getchar得到的字符是'\n'。
问题:②这一节中的代码里,scanf("%s”,a)是不会把上一次输入留下的换行符读入的,为什么?(为什么gets可以读入换行符呢呢)
在这里我解释不了,希望大家指点。
3.返回值:
gets返回值: 摘自百度:读入成功,返回与参数buffer相同的指针;读入过程中遇到EOF(End-of-File)或发生错误,返回NULL指针。所以在遇到返回值为NULL的情况,要用ferror或feof函数检查是发生错误还是遇到EOF。
所以返回一个指针,因此有一个语句应该这样写:
while (gets(c)!=NULL)
而不是
while (gets(c)!=EOF)
4.gets函数的危害:
摘自百度: 本函数可以无限读取,不会判断上限,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值。这个事实导致gets函数只适用于玩具程序,为了避免这种情况,我们可以用fgets(stdin) (fgets实际上可以读取标准输入(即大多数情况下的键盘输入),具体参阅fgets词条)来替换gets()。在V7的手册(1979年)中说明:为了向后兼容,gets删除换行符,gets并不将换行符存入缓冲区。
顺水推舟,介绍一下fgets和gets_s,
gets_s:
摘自百度:
#include <stdio.h> //这个头文件包含gets()函数,这个函数在ISO/IEC 9899 2011(C11)标准中被移除
int main(void)
{
char str1[5]; //不要char*p,然后gets(p),这是错误的,因为p没有指向有效的内存,它可能指向任何非法地址 // 地方的未知大小的内存块,这样以来,就可能修改了不属于本程序的内存的内容
gets(str1);
printf("%s\n", str1);
return 0;
}
#include <stdio.h>//gets_s()用法
#define CH 20
int main(void)
{
char ch[CH];
printf("请输入你的名字:\n");
gets_s(ch,CH); //这里不能用gets_s(ch);
printf("这是你的名字:%s\n", ch);
return 0;
}
反正我的编译器版本还没跟上这个函数
fgets:
摘自百度:
函数原型
char *fgets(char *buf, int bufsize, FILE *stream);
参数
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明存储数据的大小。
*stream: 文件结构体指针,将要读取的文件流。
返回值
1.成功,则返回第一个参数buf;
2. 在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL;
3. 如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。
fgets相比于gets有一个显著的差别就是fgets会将行末的换行符算到读入的字符串里面。所以相同且正常(输入无错误,缓冲区够大)的情况下,fgets读入的字符串会比gets在末尾'\0'前面多一个换行符
代替gets:
char a[4];
fgets(a,sizeof(a),stdin);
好处:读取指定大小的数据,避免gets函数从stdin接收字符串而不检查它所复制的缓存的容积导致的缓存溢出问题。
但是,这里有一个问题,fegets会不会读上一个换行符?
和gets一样,会,解决办法就是刷新缓冲区(fflsuh(stdin))或者加一个getchar().