一个关于函数内部指针参数返回的错误调试及分析
在C语言中如下两个概念是非常重要,但又是极其容易在编程中出问题的:
1. C语言中指针存储的是一个地址,而非实际的数据项内容。
2. C语言中函数内部定义的变量的作用域或生存空间只在函数内。
下面就本人在编程中出现的一个错误代码进行分析,(这个代码出的问题就是以上2点。)除此之外提出一个gcc导致该程序费了很大功夫才找到这2个错误的bug。
01.#include<stdio.h>
02.#include<stdlib.h>
03.struct test{
04.int x;
05.int y;
06.};
07.struct test2{
08.int z;
09.struct test *w;
10.};
11.void fun(struct test2 *st)
12.{
13.struct test fst;
14.fst.x=1;
15.fst.y=2;
16.st->w=&fst;
17.printf("fun: %d %d /n",fst.x,fst.y);
18.}
19.
20.void print(struct test2 *st)
21.{
22.printf("print:%d %d/n",st->w->x,st->w->y);
23.}
24.
25.int main()
26.{
27.struct test2 *mst;
28.mst=(struct test2 *)calloc(1,sizeof(struct test2));
29.mst->w=(struct test *)calloc(1,sizeof(struct test));
30.fun(mst);
31.printf("main: %d %d/n",mst->w->x,mst->w->y);
32.print(mst);
33.return 0;
34.}
该代码的错误在于,16行处,将fun函数内的局部变量fst的地址交给了指针参数st->w,从而主函数中打印输出的时候st->w指针所指的内容由于fun函数的返回而被系统回收了。
现在看来原理非常简单。但调试该段代码却花费了本人很多时间。执行上面的程序我们得到如下的结果:
01.fun: 1 2
02.main: 1 2
03.print:2013480168 -1
在结果第1行返回正确的值1 2,并且在第2行,仍然返回的是1 2。这就让人搞不清楚了,由现在的输出怎么也不会让人联想到在fun函数里面就出了问题,因为在31行main函数中输出了正确的结果1 2,从而检查的焦点不自觉的就转移到32行调用的print函数。但该函数本来就没有任何错误,自然就陷入了麻烦之中了。
我们再看下面这个程序:
01.#include<stdio.h>
02.#include<stdlib.h>
03.struct test
04.{
05.int x;
06.int y;
07.};
08.
09.void fun(struct test *st)
10.{
11.struct test fst;
12.fst.x=1;
13.fst.y=2;
14.st=&fst;
15.printf("fun:%d %d/n",fst.x,fst.y);
16.}
17.int main()
18.{
19.struct test *mst;
20.mst=(struct test *)calloc(1,sizeof(struct test));
21.fun(mst);
22.printf("main:%d %d /n",mst->x,mst->y);
23.return 0;
24.}
同样的再14行fun函数中错误的将局部变量fst的地址给了参数指针,而主函数中该地址被系统回收,得到如下的结果:
01.fun:1 2
02.main:0 0
这个就比较明显的看出效果来,在fun函数返回后,main函数中输出的结果已经被清零了,说明fun函数部分有问题。
我们再回过头看看第一个代码,为什么会出现前面的状况,2个代码不同之处在于,第一个代码中结构体的成员本身也是指针,我们是对这个成员指针进行的操作,在fun返回后系统并没有对该成员引用的地址进行销毁(应该算是gcc的一个bug),所以在31行的main函数中返回的仍然是1 2,而32行调用print函数时,系统将保存当前的断点地址等信息,该信息恰好写入了原来fun函数分配的局部变量fst的地址,从而改变了原有的数据内容1 2,所以输出结果的第3行,得到了一个错误的结果。