一個關於函數內部指針參數返回的錯誤調試及分析
在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行,得到了一個錯誤的結果。