要注意傳遞結構變量和結構數組的區別。
21.3.1 結構變量的傳數值與傳地址值
【例21.13】分析下面程序傳數值和傳地址值的區別。
#include <stdio.h> struct List { int a ,b ; double c ; }ag ,ad ,*p ; void Add (struct List ); void disp (const struct List * ); void Add1 (struct List * ); int main ( ) { ag.a=25 ; ag.b=30 ; ag.c=45 ; ad.a=8 ; p=&ag ; disp (&ag ); Add (ag ); disp (&ag ); Add1 (&ag ); disp (p ); Add1 (p ); disp (p ); return 0 ; } void Add (struct List s ) { s.a=s.a+s.b ;} void Add1 (struct List *s ) { s->a=s->a+s->b ;} void disp (const struct List *f ) { printf (\"%3d %3d %lfn\" , f->a ,f->b ,f->c ); }
運行結果如下。
25 30 45.000000 25 30 45.000000 55 30 45.000000 85 30 45.000000
【解釋】傳遞結構變量有傳數值和地址值之分,傳遞數值不會改變實參的值,傳遞地址值是改變實參的必要條件,但不是充分條件,如disp函數的參數是指針,但不會被改變。
傳地址時,實參以地址和指針的形式賦給形參均是等價的。形參設計為指針,程序中不一定非要聲明指針參數,使用地址值即可。
對不允許改變的參數,可以設計為const類型,如disp函數。
21.3.2 結構數組傳地址值
【例21.14】演示傳遞結構數組的例子。
#include <stdio.h> struct List { int a ,b ; double c ; }arg[4] ,*p ; void Add (struct List ); void disp (const struct List * ); int main ( ) { p=arg ; arg[0].a=87 ; arg[0].b=58 ; arg[0].c=5.8 ; arg[1].a=15 ; arg[1].b=25 ; arg[1].c=2.5 ; Add (arg ); disp (p ); p->a=33 ; Add (arg ); disp (arg ); return 0 ; } void Add (struct List s ) { s[2].a=s[0].a+s[1].a ; s[2].b=s[0].b+s[1].b ; s[2].c=s[0].c+s[1].c ; * (s+3 )=* (s+1 ); // 元素整體賦值 } void disp (const struct List *p ) { int i ; for (i=0 ;i<4 ;i++ ) printf (\"%3d %3d %lfn\" , (p+i )->a ,(p+i )->b ,(p+i )->c ); }
程序運行結果如下。
87 58 5.800000 15 25 2.500000 102 83 8.300000 15 25 2.500000 33 58 5.800000 15 25 2.500000 48 83 8.300000 15 25 2.500000
【解釋】因為結構數組的名字就是結構存儲的首地址,所以用名字和指針都是傳遞的地址值,所以要特別小心,不要修改不需要改變的參數值。對不允許改變的函數參數,推薦使用const限定詞。一定要注意,所謂改變,就是被用來做左值。
對於使用結構數組作為參數的函數而言,形參既可以使用數組,也可以使用指針,只要使用的方法按照給定參數形式正確設計即可,至於程序中的實參用哪種形式進行實參與形參的結合,都是無關緊要的,因為它們都是可以正確工作的。使用中切記不要圍著函數的設計轉悠,本例清楚地演示了這個問題。
如果結構成員很多,生成副本會很費時間,這時推薦使用指針。
【例21.15】傳地址值並不改變參數的例子。
#include <stdio.h> struct List { int a ,b ; double c ; }arg[2] ; void Add (struct List * ); void disp (const struct List * ); int main ( ) { arg[0].a=87 ; arg[0].b=58 ; arg[0].c=5.8 ; arg[1].a=15 ; arg[1].b=25 ; arg[1].c=2.5 ; Add (arg ); disp (arg ); return 0 ; } void Add (struct List *s ) { int i ,sum=0 ; double total=0.0 ; disp (s ); for (i=0 ;i<2 ;i++ ){ sum=sum+s[i].a +s[i].b ; total=total+s[i].c ; } printf (\" 整數之和為:%d ,實數之和為%lf 。n\" ,sum ,total ); printf (\" 總和為:%lf 。n\" ,sum+total ); } void disp (const struct List *p ) { int i ; for (i=0 ;i<2 ;i++ ) printf (\"%3d %3d %lfn\" , (p+i )->a ,(p+i )->b ,(p+i )->c ); }
程序運行結果如下。
87 58 5.800000 15 25 2.500000 整數之和為:185 ,實數之和為8.300000 。 總和為:193.300000 。 87 58 5.800000 15 25 2.500000
【解釋】本例更清楚地演示了傳遞地址值只是改變參數的必要條件。下面將本例的傳結構數組改為傳指針,進一步說明了設計和使用的配合問題。
【例21.16】在下面的參數傳遞中,能否改用f1(arg)的形式?舉例說明如何改寫函數才能使用結構參數。
#include <stdio.h> struct List { int a ,b ; char ch ; double z ; } arg[4] ,*p ; void fl (struct List * ); int main ( ) { arg[1].a=1000 ; arg[0].z=98.9 ; printf (\"input arg[1].z=\" ); scanf (\"%lf\" ,&arg[1].z ); p=arg ; fl (p ); return 0 ; } void fl (struct List *p ) { printf (\"%dn\" , (p+1 )->a ); printf (\"%f %fn\" , p->z ,(p+1 )->z ); }
【解答】假設輸入35.8,運行示例如下。
input arg[1].z=35.8 1000 98.900000 35.800000
這裡是用結構的指針作為形參傳遞給函數。雖然函數要求的是指針,但結構名arg就是結構存儲的首地址,所以本程序不需要修改,直接使用
f1 (arg )
的形式是完全正確的。
建議:在設計結構時,如果有鍵盤人機交互,應盡量避免使用字符和字符指針。使用字符串時,也要預防可能對讀取字符串產生的干擾。
注意:如果輸入的字符串中需要空格,不能使用scanf函數,可以使用gets函數。