gets,fgets,gets_s的区别分析

在最开始学习字符串时,我们最常用的输入字符串函数就是——gets();
这个函数将输入缓冲区中的数据存储到字符型数组中,以换行符结束,并丢弃末尾的换行符添加一个空字符。他解决了scanf("%s",str);以空白符为截至的漏洞。
但是这个函数有一个致命的弱点——不能对数组溢出做出相应的处理

我们都知道,在我们定义数组时先要确定数组的大小,系统会分配一段没被使用的连续空间给这个数组。在这片区域外,我们不知道其他的空间是否被使用,这就带来了一个问题——如果数组越界时,会存在安全隐患,但是gets函数并没有处理这种越界问题的机制。
比如:

#include<stdio.h>
int main()
{
        char ch[10];
        gets(ch);
        puts(ch);
        return 0;
}

在这里插入图片描述
这是一个简单的输入输出代码,但是我们在一开始定义数组时只分配了10个存储空间,而我往输入缓存区中输入了超过10的数据,gets函数将换行符之前的数据全都存进了ch数组。
众所周知,ch实际就是ch数组的头指针,gets从ch的头地址依次按内存顺序存入数据,这本身并没有问题,但是我们在定义数组ch时只关心ch可不可以存入10个数据,这10个数据之外的地方有没有存数据我们是不知道的。
如果凑巧,没有存入数据,这并没有关系;但如果存入了,就会将原来的数据覆盖。这会导致很大的安全隐患。而gets函数并没有检查是否存在这种数组越界。
由此诞生了gets的替代品。

替代品1:fgets()

特点一:指定读入的字符个数

fgets函数并不像gets那样只能从标准中读取数据,他更常用在读取文件中的数据。
fgets()需要传入三个参数,依次是要读入的对象,读入字符的最大数量,要读入的文件。
如果是要在标准输入中读入,将第三个参数设置为stdin(在stdio.h)中。
示例如:

#include<stdio.h>
int main()
{
        char ch[10];
        fgets(ch,10,stdin);
        fputs(ch,stdout);        // 与fgets配套的输出函数
        return 0;
}

在这里插入图片描述
输出结果显示,fgets函数只会将第二个参数指定大小的字符读入字符数组中(包括换行符)。如果输入不足10就输入换行符,则将字符包括换行符读入字符数组中。

特点二:不会将字符串末尾的换行符丢弃再添一个空字符

#include<stdio.h>
int main()
{
        char ch[10];
        fgets(ch,10,stdin);
        fputs(ch,stdout);
        return 0;
}

在这里插入图片描述
还是输入输出的程序,这次我们输入的字符数小于可读入的最大字符数。我们依次输入了 ‘a’,‘b’,‘c’,’\n’ ,fgets将这四个字符都读入到了数组ch中,与gets有区别的是,他没有将末尾的换行符丢弃并添加一个空字符 ‘\0’ ,而是直接将末尾的换行符保留下来了。
如果要将换行符换成空字符,需人为进行操作。
操作如下(将 ‘\n’ 换成 ‘\0’ ):

#include<stdio.h>
int main()
{
        char ch[10];
        fgets(ch,10,stdin);
        
        // 将 '\n' 换成 '\0'
        int i = 0;
        while(ch[i] != '\n')
                i++;
        ch[i] = '\0';
        

        fputs(ch,stdout);
        return 0;
}

在这里插入图片描述
上述代码中我们将末尾的 ‘\n’ 换成了 ‘\0’ ,输出数组ch时,没有再输出一个换行符。

特点三:保留多余字符

通过之前的例子我们已经知道,fgets函数最多只会读入指定的字符数,那那些没有读入的字符会何去何从呢?
先看下面的例子:

#include<stdio.h>
int main()
{
        char ch[10],sh[10],dh;
        // 输入
        fgets(ch,10,stdin);
        fgets(sh,10,stdin);
        dh = getchar();

        // 输出
        fputs(ch,stdout);
        putchar('\n');             // 分割 ch 和 sh
        fputs(sh,stdout);
        putchar('\n');             // 分割 sh 和 dh
        putchar(dh);
        return 0;
}

在这里插入图片描述
我们先输入了一句话 “i will ba a niubide girl” 。头10个字符是 “i will ba”,fgets将他读入到数组ch中。紧接着的10个字符 “ a niubid” ,fgets将他读入到数组sh中,后面的 ‘e’ 读入到 dh 中。后面的字符 “ girl” 我相信他也没用被丢弃,而是还在输入缓存区中。由此我们看出,fgets并没有将没有读入的数据丢弃,他们依然在输入缓存区中等待着之后的数据的读取。
有时你想将之后的数据进行丢弃。如下操作:

#include<stdio.h>
int main()
{
        char ch[10],sh[10];
        fgets(ch,10,stdin);

       // 丢弃数据
        while(getchar() != '\n')
                continue;

        // 丢弃数据后再读入一个数据
        fgets(sh,10,stdin);

        // 输出两个数组
        fputs(ch,stdout);
        putchar('\n');              // 分割 ch 和 sh
        fputs(sh,stdout);
        return 0;
}

在这里插入图片描述
提醒:最后一行的换行是因为 sh 保留了换行符,再进行输出得来的。

替代品二:gets_s()

特点一:丢弃换行符而不是保存他

对于末尾换行符的处理,在不超过数组范围时 gets_s 丢弃他而不是保存他,这点和 gets 一样和 fgets 不一样。

特点二:将多余的字符丢弃

当我们向输入缓存区中输入多于数组的字符数时,gets_s会将多余的字符丢弃以达到避免存在安全隐患的目的,不论你是否需要后面的字符。

.
.
.
.
.

比较三个函数:

情况一:目标存储区装得下输入行

这时三种情况都差不太多,有区别的是 gets 和 gets_s 会丢弃 ‘\n’ 添加一个 ‘\0’ ,而 fgets会保留末尾的换行符。

情况二:目标存储区装不下输入行

gets 会将数据全都读入数组,从数组的头地址开始依次读入。若越界后有数据则会覆盖原始 数据,从而存在安全隐患。
fgets 只读入指定的数据大小,剩下的数据依然在输入缓冲区中。
gets_s 只读入数组最大可读大小,剩下的数据全部丢弃。

欢迎大家指正和补充。

  • 16
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值