问题复现
在尝试学习c语言中出现了以下的问题:源代码如下图所示
#include <stdlib.h> /*引入标准库头文件*/
#include <stdio.h> /*引入输入输出库头文件*/
int main()
{
char message[100]={'\0'};/*定义字符数组,可存储 99 个字符*/
gets_s(message); /*输入最多 99 个字符的一句话*/
puts(message); /*将此句话打印到显示器*/
system("pause"); /*暂停并显示:显示请按任意键继续...*/
return 0;
}
这个代码在MICROSOFT VISUAL STDIO 2022中编译正常运行(如下图)
但是在MICROSOFT VISUAL STDIO 2010中编译这个软件会出现如下的报错:
报错代码:
c:\documents and settings\administrator\my documents\visual studio 2010\projects\66666\66666\haha.c(6): error C2198: “gets_s”: 用于调用的参数太少
问题解决方案
找出出问题的那一行
gets_s(message); /*输入最多 99 个字符的一句话*/
是这一行出现的问题,根据度娘的回答,gets_s()函数有如下的参数需求
gets_s(a, b); (a代表了输入的字符数组,b代表了最大可以接受的\读取的字节长度)
将老师给的代码在visual stdio改为如下即可:
#include <stdlib.h> /*引入标准库头文件*/
#include <stdio.h> /*引入输入输出库头文件*/
int main()
{
char message[100]={'\0'};/*定义字符数组,可存储 99 个字符*/
gets_s(message,101); /*输入最多 99 个字符的一句话*/
puts(message); /*将此句话打印到显示器*/
system("pause"); /*暂停并显示:显示请按任意键继续...*/
return 0;
}
问题原因分析
为什么get_s()有两个参数
这个问题还得牵扯到C语言的一个非常普遍以及致命的问题:缓冲区溢出问题。
首先c有如下的从终端输入字符的函数:gets()、get_s()等等,这里我们暂且只讨论这两个。
首先是gets()函数,这个函数从终端读取内容,但是并没有判断读取到的的数据的大小,假如读入的数据的大小大于传到的目标变量的大小(即读取的数据的量超过了之前定义的变量最大能够承受的范围),那么数据就会在内存中溢出(就像一杯水溢出来了),并且有可能导致覆盖掉程序后面的运行堆栈以及内存中其他的东西(栈溢出错误),正常情况下程序大概率会出错退出,或者什么都没有发生,但是,但是,但是!假如输入的是一个攻击者精心编写好的一串字符,这串字符刚好能够完美的准确的覆盖掉后面的数值,并且这串字符能够伪造返回数值,促使程序执行恶意的代码,玩意这个程序还是在root下运行的,哦吼,完蛋。(本篇博文并不是主要讲内存溢出问题的,对此讲解不够细致,具体请文问度娘)
所以为了避免这个情况,人们编写了get_s()函数,这个函数的第二个参数决定了能够输入的最大的数据的长度的大小,假如一旦超过,get_s()函数就会诱发错误,这好歹比发生安全漏洞好很多。
而在visual stdio 2010中get_s()没有第二个参数就会发生编译的问题
为什么visual stdio 2010中只有一个参数会报错,但visual stdio 2022中不会?
这个我也不好妄下定论,但是以下是我的分析,如有谬误请联系我
首先在visual stdio2010中,gets()函数仍然是可用的,但是visual stdio2022中则不再可用,如下图。
经过我的查询,2011年出现了这样一件事情,“2011年12月,ANSI 采纳了 ISO/IEC 9899:2011 标准,标准中删除了 gets()函数,用函数gets_s()替代”,而我们使用的是visual stdio2010年版本,所以没有被更改,
我认为,在2010中,能使用gets()函数传入,那么假如是使用gets_s()函数,那必定是要限制传入的数据的大小,不然就直接使用gets()函数就可以了,所以还不如设计为只传入一个参数直接就报错了,但是在visual stdio2022版本中,gets()函数连骨灰都没了,所以万一有人喜欢使用老式的gets()语法,不想思考缓冲区溢出的安全问题,所以设计为传入一个参数也可以。以上是我的猜测,如有问题请指正,谢谢。