可以將結構作為函數的參數,對結構進行操作。不要覺得要修改結構的值,就一定將結構作為地址值傳遞,這決定設計與使用函數的具體方法。
【例21.22】下面是把兩個結構域的值相加作為另一個結構的域值供主程序使用,分析沒有實現預定目標的原因,並修改程序實現預定功能。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; Add (d ,e ,f ); // 傳結構變量的數值 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 將結構作為參數,以傳數值的方式傳遞這個參數 void Add (struct LIST d ,struct LIST e ,struct LIST f ) { f.a=d.a+e.a ; f.b=d.b+e.b ; }
【解答】程序將Add函數的結構f作為傳數值的方式傳遞,當函數返回時,主程序裡的參數不會被修改(f.a=f.b=0),所以沒有完成預定功能。
可以有三種解決這個問題的方法。
1.改變參數f的傳遞方法
可以不修改Add函數的返回類型,即保留void類型,修改參數f的傳遞方法,將傳數值改為傳地址值,即將這個參數以指針方式傳遞。
// 修改後的程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ,struct LIST* ); void main () { struct LIST e={5} ,f={0} ; Add (d ,e ,&f ); // 傳結構變量f 的地址 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 將結構f 作為參數,以傳地址值的方式傳遞這個參數 void Add (struct LIST d ,struct LIST e ,struct LIST *f ) { f->a=d.a+e.a ; f->b=d.b+e.b ; }
因為e.b=0,主程序中f的f.a=f.b=8,f.a+f.b=16。
2.將函數返回類型改為返回結構
可以不改變參數類型,而是將函數vid的返回類型改為struct類型。讓Add函數返回結構f,使用「f=Add(d,e,f);」語句,實現對主函數結構f的修改。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=Add (d ,e ,f ); // 用結構f 接收返回值 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 將結構作為參數,以傳數值的方式傳遞這個參數 struct LIST Add (struct LIST d ,struct LIST e ,struct LIST f ) { f.a=d.a+e.a ; f.b=d.b+e.b ; return f ; }
3.將函數返回類型改為返回結構指針
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=* (Add (d ,e ,f )); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
一般是要在主程序中設計一個指針以接受函數返回的指針,例如
struct LIST *p ; p=Add (d ,e ,f );
這種情況一般是在主程序中使用指針變量p,在本程序中,等於沒有修改f的值域,這就要使用
printf (\"p->a+p->b=%dn\" , p->a+p->b );
語句,這不合題意。為了修改f,所以直接使用
f=* (Add (d ,e ,f ));
語句。因為「Add(d,e,f)」返回的是指針,所以「*(Add(d,e,f))」引用的是返回指針變量的值。所以在主程序中也沒有使用指針的必要了。
不過,這種方法顯得累贅,不如直接將f作為地址值傳遞簡單,也就是第1種方法簡單。所以說,要結合具體情況,選擇最優設計。
4.需要注意的問題
這個程序完全是為了說明問題,針對這個程序,還有兩個要注意的問題。
如果不給Add函數里的指針分配地址,則會出現一些問題。假如使用如下方式:
struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p ; p=&f ; p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
表面上看來似乎可行。其實Add返回的指針是Add函數內的臨時指針,也是不可靠的。這個地址裡的值隨時都會發生變化,運行結果甚至依賴主程序語句執行的順序。
// 不可靠的示範程序 #include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; struct LIST *p=NULL ; p=Add (d ,e ,f ); printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); //1 printf (\"p->a+p->b=%dn\" , p->a+p->b ); //2 f=*p ; printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p=&f ; p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
輸出的錯誤結果如下:
p->a=8 ,p->b=8 p->a+p->b=16 f.a=4341788 ,f.b=16 f.a+f.b=4341804
在第1次執行1~2打印語句時,結果正確。其實,這時候p指向的地址內容已經發生了變化,所以導致「f=*p;」賦值的結果錯誤,後面的輸出當然也就錯了。再看看下面主程序的運行結果就更清楚了。
void main () { struct LIST e={5} ,f={0} ; struct LIST *p=NULL ; p=Add (d ,e ,f ); f=*p ; printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); //1 printf (\"p->a+p->b=%dn\" , p->a+p->b ); //2 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); printf (\"p->a+p->b=%dn\" , p->a+p->b ); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); }
運行結果如下:
p->a=8 ,p->b=8 p->a+p->b=16 f.a=8 ,f.b=8 f.a+f.b=16 p->a=4345840 ,p->b=16 p->a+p->b=4345856 f.a=8 ,f.b=8 f.a+f.b=16
先賦給f,因為f有自己的存儲地址,所以兩次打印的結果相同,但兩次使用指針的結果就不一樣了。所以一定要注意地址問題。
其實,Add的第3個變量是沒有必要的,下面給出滿足使用結構f的一種可靠的方法。
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=* (Add (d ,e )); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }