映月读书网 > 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函数。