要注意传递结构变量和结构数组的区别。
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函数。