讀古今文學網 > C語言解惑 > 21.3 使用結構作為函數的參數 >

21.3 使用結構作為函數的參數

要注意傳遞結構變量和結構數組的區別。

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函數。