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