gets()的结构性缺陷和补救措施

本文探讨了C语言中gets函数的安全问题,由于可能导致缓冲区溢出,它已被视为不安全。建议使用fgets和gets_s作为替代。fgets允许指定最大读取字符数以防止溢出,而gets_s是C11标准引入的更安全选项,限制读取长度并丢弃超出的字符。然而,gets_s在字符过长时会丢弃早期数据,可能不适用于所有情况。

gets与系统安全问题

gets函数的存在意义在于取代scanf,因scanf和转换说明%s只能读取一个单词,遇到空符读取就会终止,如要读取一整行输入scanf就显得捉襟见肘。而gets可以解决这个问题:gets可以读取整行输入直至读取到换行符,后丢弃换行符,存储字符,在末位加一个空符形成字符串。

gets的局限在于其无法检查数组是否装得下输入行,因此gets只能查出数组的初始地址却查不出数组元素数。若输入字符超出字符串定义的容量会导致缓冲区溢出,可能危害系统安全,尤其是在内存被占满的情况下。

此时输入的字符数已经超出了定义时所规定的元素数,但dev没有发出警告。

替代方案

fgets()

fgets通常用于替代gets。

char *__cdecl fgets(char * __restrict__ _Buf,int _MaxCount,FILE * __restrict__ _File);
fgets(目标字符串变量,字符数,来源(文件));

fgets通过添加第二个参数限制字符数来处理溢出问题。该函数专门用于处理文件输入。

与fgets的区别:

·fgets比起gets多了两个参数,其中一个参数决定了读入字符的最大量。若参数值为n,fgets将最多读入n-1个字符(第n个位换行符)或者在之前读到一个换行符就终止读取。

·fgets同比gets会把换行符保留在字符串中,gets会丢弃换行符。

fgets的第三个参数指明要读入的文件。

fgets() 函数会在遇到换行符时停止,并且其保存到内存中的数据会包含该换行符,而 gets() 函数会排除换行符。因此,就简单的这么重写代码无法实现完全同等的功能。而为了保证代码安全,又实现完全相同的功能,我们就需要检查内存地址中的字符,如果在结尾有换行符,需要将其删除。

char *last = input strlen(input) – 1;
if (*last == 'n')
    *last = '';

可是,虽然代码变复杂了,但是还是存在一个隐藏问题,该问题会导致程序崩溃,或者有安全隐患。当程序执行时,如果标准输入流已经得到了所有可用的字符,但是还没有遇到文件结束符( EOF), fgets() 函数将会通过将 input[0] 标记为 NULL 字符的形式,直接返回一个 NULL 字符串。此时, strlen(intput) 的返回值为0, 因此导致 last 指针指向 input 数组之前的那个字符。因为不能确定这个字符到底是什么,这段代码的行为将因此无法判断 。

gets_s()

gets_s为C11新增,与fgets类似,用一个参数限制读入字符数。

与fgets的区别如下:

*gets_s只从标准输入中读取数据,不需要第三个参数

*丢弃而不是存储换行符

*若gets_s读取至最大字符数都没有读取至换行符,会把最早读取的字符数据丢弃,读入接下来的数据直至读到换行符或文件结尾,后返回NULL。

因此若输入行未超过最大字符数则可用gets_s取代fgets。

gets_s安全性也高于gets,但如果字符过长,gets_s会丢弃早期读取的字符,无论是否需要。这意味着gets_s的指令不方便也不够灵活。

在 C 语言中,`gets` `fgets` 都用于从输入流读取字符串,但它们在安全性、功能使用方式上有显著区别。 --- ### `gets` 函数 ```c char *gets(char *s); ``` - 从标准输入(`stdin`)读取一行字符,直到遇到换行符或文件结束符。 - 将换行符转换为字符串结束符 `'\0'`。 - **不检查缓冲区溢出**,如果输入的字符串长度超过数组容量,会导致越界写入。 - **已被弃用**,C11 标准中已移除,使用它会产生安全警告。 #### 示例: ```c char a[100]; gets(a); // 危险!无法防止缓冲区溢出 ``` --- ### `fgets` 函数 ```c char *fgets(char *s, int n, FILE *stream); ``` - 从指定文件流(如 `stdin`)读取最多 `n-1` 个字符,保留 `'\0'` 空间。 - 会将换行符 `'\n'` **保留在字符串中**(如果空间足够)。 - 安全,可防止缓冲区溢出。 - 推荐替代 `gets` 的方法。 #### 示例: ```c char a[100]; fgets(a, 100, stdin); // 安全,最多读取99个字符 + '\0' ``` --- ### 主要区别对比 | 特性 | `gets(s)` | `fgets(s, n, stdin)` | |------|-----------|------------------------| | 是否安全 | ❌ 不安全,易导致缓冲区溢出 | ✅ 安全,限制读取长度 | | 是否保留 `\n` | ❌ 换行符被丢弃 | ✅ 换行符可能被保留 | | 最大读取长度 | 无限制 | 最多 `n-1` 个字符 | | 当前标准状态 | 已废弃 | 推荐使用 | --- ### 替代方案建议 由于 `gets` 存在严重安全隐患,应始终用 `fgets` 替代: ```c // 推荐写法:使用 fgets 并手动去除可能的换行符 char a[100]; if (fgets(a, 100, stdin) != NULL) { size_t len = 0; while (a[len] != '\0') len++; if (len > 0 && a[len-1] == '\n') a[len-1] = '\0'; // 去除换行符 } ``` 这样既保证了安全性,又实现了与 `gets` 类似的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值