要注意传递结构变量和结构数组的区别。
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.00000025 30 45.00000055 30 45.00000085 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.500000102 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.80000015 25 2.500000整数之和为:185,实数之和为8.300000。总和为:193.300000。87 58 5.80000015 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.8100098.900000 35.800000
这里是用结构的指针作为形参传递给函数。虽然函数要求的是指针,但结构名arg就是结构存储的首地址,所以本程序不需要修改,直接使用
f1(arg)
的形式是完全正确的。
建议:在设计结构时,如果有键盘人机交互,应尽量避免使用字符和字符指针。使用字符串时,也要预防可能对读取字符串产生的干扰。
注意:如果输入的字符串中需要空格,不能使用scanf函数,可以使用gets函数。