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的指令不方便也不够灵活。