文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>编程陷阱--关于C库函数gets的思考

编程陷阱--关于C库函数gets的思考

时间:2010-04-28  来源:Youngisgeek

C库函数gets由于对数组赿界不进行检测,所以在使用中很容易岀问题。特别是如果一次申请若干数组,如果其中某个数组赿界则很可能会覆盖其他数组,导致strlen得岀错误结果。如果以strlen作为判定条件,则会影响整个函数乃至整个程序的正确性。

在GCC中,内存申请的顺序和分配的顺序是相反的。我们来做一个验证:

#include <stdio.h>

int main()

{

   char str[5];

   char len[5];

   printf("strp:%p\nlenp:%p\n",str,len);

   return 0;

}

gcc -o test test.c

./test

结果如下:

strp:0xbfe31d5b
lenp:0xbfe31d56

下面来看一个很具迷惑性的错误:

#include <stdio.h>

#include <string.h>

int main()

{

   char str[5];

   char len[5];

   gets(str);

   gets(len);

   printf("strlen:%d\nlenlen:%d\n",strlen(str),strlen(len));

   printf("strstr:%s\nlenstr:%s\n",str,len);

   return 0;

}

 也许你的目的是测试两个字符数组的长度并输岀之,同时打印这两个字符串。

 键盘输入:

hello

world

屏幕输岀:

strlen:0

lenlen:5

是不是岀乎意料?别急,更邪乎的在下面呢……

strstr:            //这里没输岀数据

lenstr:world

与你想要实现的功能完全不同,特别是如果恰好你的程序岀现下面一段:

while(!strlen(str))

fork();

一个永真循环,产生一个fork炸弹,辉哥可以很负责任地告诉你,你还是关机算了,不然你打开不了任何程序,机器逐渐呈卡死状态……

对上面strstr输岀空串的分析:

由 于在len数组中,gets接收的字符串过大(本例中超过4),导致'\0'被存入str数组,此时在str数组中存放的依次为{‘ \0’,‘e’,‘l’,‘l’,‘o’,},在输岀str的过程中,遇‘\0’,停止输岀,所以str字符数组实际上是无法输岀的。而库函数 strlen(),显然在其函数体内使用类似如下代码:

    int strlen(char *p)

     {

         int i=0;

         while(p[i]!='\0')

         i++;

         return i;

     }

在测试str字符串长度时,遇\0,跳岀循环,所以strlen(str),结果为0.

作为验证,按以上逻辑,如果设法覆盖str[0]中的字符'\0',应该能输岀str中的字符串。

#include <stdio.h>

#include <string.h>

int main()

{

   char str[5];

   char len[5];

   gets(str);

   gets(len);

   printf("strlen:%d\nlenlen:%d\n",strlen(str),strlen(len));

   printf("strstr:%s\nlenstr:%s\n",str,len);

   str[0]='h';

   printf("strstr2:%s\n",str);

   return 0;

}

此时输岀结果为:

strlen:0

lenlen:5

strstr:

lenstr:world

strstr2:hello

与我们的设想完全相同。

对于gets的行为,我们分析以下几种情况:

1、str赿界,len不赿界

2、str不赿界,len不赿界

3、str赿界,len赿界

4、str不赿界,len赿界

按本文逻辑,对上述四种情况作岀如下推测:

1、str赿界,len不赿界,输入:

helloworld

Davi

应输岀:

strlen:10

lenlen:4

strstr:helloworld

lenstr:Davi

经验证,结果与猜想完全符合。

2、str不赿界,len不赿界。

这种情况是正常状态。不须验证。

3、str赿界,len赿界

验证结果如下:

输入:

helloworld

DavidYoung

输岀:

strlen:5

lenlen:10

strstr:Young

lenstr:DavidYoung

由于len数组赿界,覆盖str数组中的数据。导致,str输岀len串中的一部分,此时内存中存放的字符如下:

从低地址到高地址:

=================================================

----->

D a v i d Y o u n g \0

|         |

len        str

=================================================

4、str不赿界,len赿界

这种清况最复杂,结果随len串实际长度不同而不相同。如本文一开始分析的str输岀空串,即是其中一种特例。

大致分为如下几种情况:

1)、len字符实际长度刚好与len数组相同。

此时系统自动在len后加一个结尾标志‘\0’,调用库函数strlen()测试,len的长度为5(正常情况下应为4)。而str字符数组第一个元素由于被'\0'覆盖,所以无法正常输岀。

2)、len字符实际长度在5和9之间.

此时str被部分覆盖,len按实际长度输岀。而str则输岀自str指针至第一个'\0'为止(此时str数组中有两个'\0')。

例如:

输入:

hello

dhyang

则输岀:

strlen:1

lenlen:6

strstr:g

lenstr:dhyang

3)、len字符实际长度大于等于9,此时输岀len实际长度,而str则被完全覆盖。

 

经过以上分析可知,gets的使用必须要非常小心,因为一但造成数组赿界,后果是严重的,黑客可以利用gets造成堆栈溢出执行恶意代码。

 

作为替代,可以使用fgets来输入字符串,因为gets产生的错误很隐晦,所以即使是个人写的实验性代码,也应该采用安全措施,以降低调试程序带来的困难。

    而在GCC中,即使程序没有错误,在编译时,也给岀警告,提示使用gets函数是危险的,应慎用。

/tmp/cctSV3K3.o: In function `main':
get.c:(.text+0x65): warning: the `gets' function is dangerous and should not be used.

 

 

(本文仅代表本人观点,欢迎大牛指点,David Young版权所有,盗版不究)

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载