当我们要对数组进行输入一串字符时,我们可能会用scanf()输入,但是很多时候会图方便用gets()函数,但是这个函数是有弊端的,我们就来看这个弊端在哪。
/* Demonstration of buffer overflow */
#include <stdio.h>
#include <stdlib.h>
/* Implementation of library function gets() */
char *gets(char *dest)
{
int c = getchar();
char *p = dest;
while (c != EOF && c != '\n') {
*p++ = c;
c = getchar();
}
*p = '\0';
return dest;
}
/* Read input line and write it back */
void echo()
{
char buf[4]; /* Way too small! */
gets(buf);
puts(buf);
}
void call_echo()
{
echo();
}
int main()
{
printf("Type a string:");
call_echo();
return 0;
}
在Linux上运行结果:
hxl@hxl-virtual-machine:~/桌面/task/code$ ./b
Type a string:123
123
hxl@hxl-virtual-machine:~/桌面/task/code$ ./b
Type a string:1234
1234
hxl@hxl-virtual-machine:~/桌面/task/code$ ./b
Type a string:12345
12345
*** stack smashing detected ***: <unknown> terminated
已放弃 (核心已转储)
上述代码是用来显示gets()函数出错的。首先看看gets()函数是怎么写出来的。变量c通过getchar()函数接收从控制台输入的字符,将数组的首地址给指针p,一个循环判断如果c不为换行符\n,不为文件结束符EOF,则进行循环,将c给指针p所指的地址存储空间,指针p后移。这就是gets()函数的大体,这就有个问题了,为什么没有检测指针p是否还在数组地址内,也就是没有检测数组是否越界,主函数通过调用 call_echo(), call_echo()再调用 echo(),这个函数定义一个数组,以及调用gets()和puts()函数。函数调用时会将返回地址入栈,但是一旦数组越界,就会破坏这个返回地址,这样一来,返回的路就找不到了,就出现了上面的“已放弃 (核心已转储)”,但是puts()函数照常执行,破坏的是call_echo()返回到 echo()函数的地址,返回去的代码就不会执行了。
总结:
gets()函数不会越界检测,可能破坏栈中有用的地址,比如返回地址,这样就会导致调用函数后不能返回到调用的地方。所以尽量避免使用gets()函数。