当前位置: 代码迷 >> 综合 >> 字符串的输入输出函数(gets、fgets、gets_s及对应的输出函数)
  详细解决方案

字符串的输入输出函数(gets、fgets、gets_s及对应的输出函数)

热度:59   发布时间:2023-12-12 23:59:35.0

首先声明例子都是char数组且足够大,如果是指针就需要手动分配空间。

scanf函数

这个不用说了吧,scanf("%s",a);就能将输入存储到a这个数组里面。

问题
scanf函数遇到空字符就会结束,(空字符有空格、回车、tab键等等),所以当你输入 i love you 时,数组内存储的就是一个i了,这样的程序七夕节当然是不能用的了,所以接下来就产生了一些其他的函数。

  • 注意是从第一个非空字符开始的,所以" i love you"读入的也是i;
  • 还可以用%10s这样的数字限定读取长度,前面的就是读取十个字符,所以注意数组大小别玩脱了。
  • 还有scanf返回值是成功读取的个数或者EOF(失败读入以及达到文件结尾。)

上述具体内容在本人的scanf函数博客中有详细介绍。

gets() (对应的输出为puts() )

emm其实这个是一个残次品,不过还是有自己的优点的,嗯就是方便,但还是不要用了。

原则:读取一行输入,直到回车,然后抛弃回车,puts(a) == printf("%s\n",a),在末尾添加换行符

但是有的时候编译器会给出警告,因为这个参数是相当于一个数组的首地址而不知道这个数组的大小,所以可能造成越界存储。
这样的漏洞就可能被人利用,对内存进行修改从而做出破坏行为。
我们老师之前就有一个例子,输入的密码,如果输入过多将正确的密码覆盖,就可以将密码改为一个自己知道的内容,如:
/*
输入六个字母的密码:(假设正确的为aaaaaa)
abcdefabcdef
密码错误,请重新输入:
abcdef
密码正确
*/
上述案例中因为存储输入的密码产生越界,导致原来的密码被abcdef覆盖,此时就可以强行打开密码了。

然后呢,人们就废除了这种方式,,开始是C99建议不要使用,后来就被C11禁止了。
(虽然一部分编译器为了兼容还是支持gets函数的)

很巧,codeblocks 20.03就支持这一函数(最狗的是还没有警告),而且我也做了测试,所谓的丢弃是直接读取后删除,而不是留在缓冲区内被下一个字符读取。
在这里插入图片描述

fgets()函数

作为gets的替代品,fgets函数出现了,对应的输出是fputs函数。
区别

  • fgets函数有多个参数,第一个是数组名,第二个是长度(n),当读到一个回车或者是读满n-1个字符,结束读入;
  • fgets函数会读入换行符,存储在字符串中,而不是和gets函数一样丢弃换行符
  • 因为前面的f字母,我们很自然就会联系到文件,所以函数的第三个参数就是文件,有文件就写文件名,没有就写一个stdin上去(stdio.h文件中的一个标识符)。个人认为stdio中的i和o分别代表in和out,即输入输出流,具体本人没有深究,感兴趣的读者可以参考一下文献,记得在下方留言哦~(老公众号了)

同样,fputs函数第一个参数也是数组名,第二个则是文件名,没有文件写stdout。(看来没有猜错)
但是fputs函数没有添加换行符的行为,因为只要fgets函数不填满,后面都是有换行符的。

返回值问题:
fgets函数的返回值是一个char指针,正常情况下是数组的首地址,但如果读到换行符或者文件结尾,返回值为NULL(空指针)。

不足:
比如我希望输入两行内容,但是每一行都超过了数组限定大小10,但是我还不想要多余的部分,所以会出现下面的情况:
在这里插入图片描述
第二个数组接着上面读取,两个输出连在一起。所以就需要用while循环吃掉缓冲区剩下的内容。
因为有可能下面读入的是int、double型,要是还留着自己不想要的,就麻烦了。

while(getchar() != '\n');//注意这个分号,其实写成continue更方便看

gets_s函数

C11将gets函数废掉了,那么就必须要提出一个新的函数代替,,不过这个函数只是编译器可选的
(俗称看心情决定有没有,我目前的cb20.03貌似就没有)

和fgets函数的区别

  • 只从标准输入内读取,所以不涉及文件操作,也就相对于fgets函数没有第三个参数;
  • 丢弃换行符
  • 最后一个是关于读取超过最大限制的,这个先放一下,因为和其余的函数都不一样。

所以如果不超过限定长度,gets_s函数和fgets函数也差不多。
在超出限定的时候,gets_s函数确实很安全,安全到头疼,书上是这样写的:将数组的首字符改为空字符,然后将一行或者文件剩下的内容读取并删除,接着调用特殊函数(可以是自己写的),如果没有自己写,就可能中止程序甚至退出程序。

他也没声明那个特殊的处理函数是什么鬼。。。

但至少我们有这样的结论了,在越界的情况下,gets函数就算了吧,谁知道能搞出来什么;gets_s函数太烦,还是fgets函数好亿点点。

s_gets函数

在C primer Plus 书中给出了这样一个函数,是读入一行文字,将换行符改为空字符;如果超出范围就舍弃剩下的部分。
其实看起来就是fgets函数的加强版,处理了换行符的问题和一行没读取结束后的不足(上面的图片)

char *s_gets(char *st, int n)
{
    char *ret_val = fgets(st, n, stdin);int i = 0;if(ret_val)//不是空指针说明读入成功。{
    while(st[i] != '\n' && st[i] != '\0')//找换行符i++;if(st[i] == '\n')//读入中有换行符,将换行符修改为空字符st[i] = '\0';else//没有换行符,说明没读完,要清除缓冲区(一行的内容就好)while(getchar() != '\n')continue;//好叭我没有用一个引号}return ret_val;
}

接下来讨论输出函数的细节。

puts()

这个,和gets差不多都有一点残废。
给一个char型指针就开始输出,一直到遇到空字符
所以puts(&a[5]);就是从a[5]开始输出,但如果没有空字符,程序就不知道跑到哪里去了。。。
会在输出结束后加一个回车。

fputs()

两个参数,一个是数组名,另一个文件名或者是stdout(打印到显示器)
而且人家不会添加换行符。

注意一下,名字都对应着起了,意思也很明显就是希望使用者的输入输出按照一对的函数来。

  相关解决方案