結構可以作為函數的參數,函數可以不返回值(void類型),也可以返回結構或者結構指針。
【例9.6】下面是把結構d的域值加到f的域值上的例子,分析沒有實現預定目標的原因。不修改Add函數的類型,實現程序功能。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ); void main () { struct LIST f={5} ; Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } void Add (struct LIST d ,struct LIST f ) { f.a=d.a+f.a ; f.b=d.b+f.b ; }
【解答】程序將Add函數的結構f作為傳數值的方式傳遞,當函數返回時,主程序裡的參數不會被修改。因為要求不修改Add函數的類型,實現程序功能,所以應該修改參數傳遞方式,即將傳數值改為傳地址值(將這個參數以指針方式傳遞)的方式。
// 修改後的程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST* ); void main () { struct LIST f={5} ; Add (d ,&f ); // 傳結構變量f 的地址 printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } // 將結構f 作為參數,以傳地址值的方式傳遞這個參數 void Add (struct LIST d ,struct LIST *f ) { f->a=d.a+f->a ; f->b=d.b+f->b ; }
因為f.b=0,所以f的f.a=f.b=8。
【例9.7】下面是把一個結構域的值加到另一個結構相應的域值中,編譯沒有錯誤,但沒有實現預定功能。請分析原因並修改程序。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST Add (struct LIST ,struct LIST ); void main () { struct LIST f={5} ; Add (d ,f ); // 改為f=Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } // 將結構作為參數,以傳數值的方式傳遞這個參數 struct LIST Add (struct LIST d ,struct LIST f ) { f.a=d.a+f.a ; f.b=d.b+f.b ; return f ; }
【解答】這實際是上一個例子的解決方案。不改變參數傳遞方式,讓Add函數返回結構f,實現對主函數結構f的修改。因為語句「Add(d,f);」中的參數f在調用結束之後就消失了,所以沒有改變主程序的f值。應該將Add的返回值接收下來,即用結構f接收返回值。
f=Add (d ,f );
【例9.8】下面是將上例的函數Add改為返回指針的函數,找出存在的問題。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); int main () { struct LIST f={5 ,3} ,*p=&f ; p=Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); return 0 ; } // 將結構作為參數,以傳數值的方式傳遞這個參數 struct LIST *Add (struct LIST d ,struct LIST f ) { struct LIST *p=&f ; f.a=d.a+f.a ; f.b=d.b+f.b ; return p ; }
【解答】這個程序的錯誤很典型。在Add函數里定義的指針變量是自己局部所有的變量,在Add裡,p->a=8,p->b=11,p有自己的指向地址。在退出Add函數之後,這個局部變量p消失,主函數里仍然是原來的p,「p=Add(d,f);」只是將主程序裡p的指向改變為在Add函數里指向的地址,但是&f沒有變化,仍然是原來的值,而Add傳遞的參數是f的值,所以不改變主程序裡的值。但這時p的指向地址裡是不可預測的值。
不要以為使用
f=*p ;
語句就能使f的內容與Add返回值一樣。其實*p是不確定的值,只有p是確定的值時,才可以使用這種方法。下面的程序就是演示了這個問題。
如上所示,在Add函數里設計的局部指針變量p是使用參數f初始化,一旦退出程序就消失了。其實這是個不穩定因素,容易產生怪七怪八的錯誤。穩妥的設計是申請一塊內存。為了便於理解,下面的程序輸出它們的地址和數值供比較。
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); int main () { struct LIST f={5 ,3} ,*p=&f ; printf (\" 系統分配p=0x%x ,&f=0x%xn\" , p ,&f ); p=Add (d ,f ); printf (\" 調用後p->a :%d ,p->b :%dn\" , p->a ,p->b ); printf (\" 調用後a :%d ,b :%dn\" , f.a ,f.b ); printf (\" 調用後p=0x%x ,&f=0x%dn\" , p ,&f ); f=*p ; printf (\" 執行f=*p ;語句的結果如下。n\" ); printf (\"p->a :%d ,p->b :%dn\" , p->a ,p->b ); printf (\"a :%d ,b :%dn\" , f.a ,f.b ); printf (\"p=0x%x ,&f=0x%xn\" , p ,&f ); return 0 ; } struct LIST *Add (struct LIST d ,struct LIST f ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+f.a ; p->b=d.b+f.b ; printf (\" 在Add 函數中p=0x%xn\" , p ); return p ; }
程序運行結果如下。
系統分配p=0x12ff78 ,&f=0x12ff78 在Add 函數中p=0x4300c0 調用後p->a :8 ,p->b :11 調用後a :5 ,b :3 調用後p=0x4300c0 ,&f=0x1245048 執行f=*p ;語句的結果如下。 p->a :8 ,p->b :11 a :8 ,b :11 p=0x4300c0 ,&f=0x12ff78
由此可見,當要返回指針時,在被調函數里申請動態內存,不如直接使用一個指針參數。因為&f就是地址,所以在主程序裡根本不需要再設計指針,只要將Add函數傳遞的f改為傳遞指針即可,這樣一來,程序就變得非常簡單了。
// 將f 作為指針傳遞給Add 函數的源程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST* ); int main () { struct LIST f={5 ,3} ; Add (d ,&f ); printf (\"a :%d ,b :%dn\" , f.a ,f.b ); return 0 ; } struct LIST *Add (struct LIST d ,struct LIST *f ) { f->a=d.a+f->a ; f->b=d.b+f->b ; return f ; }
顯然,Add函數可以簡化為void類型的函數。
【例9.9】改正如下程序中的錯誤。
#include <stdio.h> #include <stdlib.h> typedef struct student { char name[10] ; int studnem ; } *STUDNT ; int main () { STUDNT PT ; if ((PT= (STUDNT ) malloc (sizeof (STUDNT )) ) == NULL ) return 1 ; printf (\" 輸入姓名和學號:\" ); scanf (\"%s%d\" , PT->name ,PT->studnem ); printf (\" 姓名:%s 學號:%dn\" , PT->name ,PT->studnem ); return 0 ; }
【解答】這裡的STUDNT是定義的新結構類型指針,所以用它定義的PT是結構指針變量,由於sizeof()要求的是結構類型,所以不能再使用sizeof(STUDNT)而使用sizeof(struct student)。同理,malloc()前面不是使用(STUDNT*)而應使用(STUDNT)。
scanf要求的是地址,name是字符串,可以不用加&,但studnem是整數類型,所以必須冠以地址符號&。
// 修改後的程序 #include <stdio.h> #include <stdlib.h> typedef struct student { char name[10] ; int studnem ; } *STUDNT ; int main () { STUDNT PT ; if (( PT= (STUDNT ) malloc (sizeof (struct student )) ) == NULL ) return 1 ; printf (\" 輸入姓名和學號:\" ); scanf (\"%s%d\" , PT->name ,&PT->studnem ); printf (\" 姓名:%s 學號:%dn\" , PT->name ,PT->studnem ); return 0 ; }
程序運行示範如下。
輸入姓名和學號: 王銀英 20983 姓名:王銀英 學號:20983