可以将结构作为函数的参数,对结构进行操作。不要觉得要修改结构的值,就一定将结构作为地址值传递,这决定设计与使用函数的具体方法。
【例21.22】下面是把两个结构域的值相加作为另一个结构的域值供主程序使用,分析没有实现预定目标的原因,并修改程序实现预定功能。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; Add (d ,e ,f ); // 传结构变量的数值 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 将结构作为参数,以传数值的方式传递这个参数 void Add (struct LIST d ,struct LIST e ,struct LIST f ) { f.a=d.a+e.a ; f.b=d.b+e.b ; }
【解答】程序将Add函数的结构f作为传数值的方式传递,当函数返回时,主程序里的参数不会被修改(f.a=f.b=0),所以没有完成预定功能。
可以有三种解决这个问题的方法。
1.改变参数f的传递方法
可以不修改Add函数的返回类型,即保留void类型,修改参数f的传递方法,将传数值改为传地址值,即将这个参数以指针方式传递。
// 修改后的程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ,struct LIST* ); void main () { struct LIST e={5} ,f={0} ; Add (d ,e ,&f ); // 传结构变量f 的地址 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 将结构f 作为参数,以传地址值的方式传递这个参数 void Add (struct LIST d ,struct LIST e ,struct LIST *f ) { f->a=d.a+e.a ; f->b=d.b+e.b ; }
因为e.b=0,主程序中f的f.a=f.b=8,f.a+f.b=16。
2.将函数返回类型改为返回结构
可以不改变参数类型,而是将函数vid的返回类型改为struct类型。让Add函数返回结构f,使用“f=Add(d,e,f);”语句,实现对主函数结构f的修改。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=Add (d ,e ,f ); // 用结构f 接收返回值 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } // 将结构作为参数,以传数值的方式传递这个参数 struct LIST Add (struct LIST d ,struct LIST e ,struct LIST f ) { f.a=d.a+e.a ; f.b=d.b+e.b ; return f ; }
3.将函数返回类型改为返回结构指针
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=* (Add (d ,e ,f )); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
一般是要在主程序中设计一个指针以接受函数返回的指针,例如
struct LIST *p ; p=Add (d ,e ,f );
这种情况一般是在主程序中使用指针变量p,在本程序中,等于没有修改f的值域,这就要使用
printf (\"p->a+p->b=%dn\" , p->a+p->b );
语句,这不合题意。为了修改f,所以直接使用
f=* (Add (d ,e ,f ));
语句。因为“Add(d,e,f)”返回的是指针,所以“*(Add(d,e,f))”引用的是返回指针变量的值。所以在主程序中也没有使用指针的必要了。
不过,这种方法显得累赘,不如直接将f作为地址值传递简单,也就是第1种方法简单。所以说,要结合具体情况,选择最优设计。
4.需要注意的问题
这个程序完全是为了说明问题,针对这个程序,还有两个要注意的问题。
如果不给Add函数里的指针分配地址,则会出现一些问题。假如使用如下方式:
struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p ; p=&f ; p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
表面上看来似乎可行。其实Add返回的指针是Add函数内的临时指针,也是不可靠的。这个地址里的值随时都会发生变化,运行结果甚至依赖主程序语句执行的顺序。
// 不可靠的示范程序 #include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; struct LIST *p=NULL ; p=Add (d ,e ,f ); printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); //1 printf (\"p->a+p->b=%dn\" , p->a+p->b ); //2 f=*p ; printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ,struct LIST f ) { struct LIST *p=&f ; p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }
输出的错误结果如下:
p->a=8 ,p->b=8 p->a+p->b=16 f.a=4341788 ,f.b=16 f.a+f.b=4341804
在第1次执行1~2打印语句时,结果正确。其实,这时候p指向的地址内容已经发生了变化,所以导致“f=*p;”赋值的结果错误,后面的输出当然也就错了。再看看下面主程序的运行结果就更清楚了。
void main () { struct LIST e={5} ,f={0} ; struct LIST *p=NULL ; p=Add (d ,e ,f ); f=*p ; printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); //1 printf (\"p->a+p->b=%dn\" , p->a+p->b ); //2 printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); printf (\"p->a=%d ,p->b=%dn\" , p->a ,p->b ); printf (\"p->a+p->b=%dn\" , p->a+p->b ); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); }
运行结果如下:
p->a=8 ,p->b=8 p->a+p->b=16 f.a=8 ,f.b=8 f.a+f.b=16 p->a=4345840 ,p->b=16 p->a+p->b=4345856 f.a=8 ,f.b=8 f.a+f.b=16
先赋给f,因为f有自己的存储地址,所以两次打印的结果相同,但两次使用指针的结果就不一样了。所以一定要注意地址问题。
其实,Add的第3个变量是没有必要的,下面给出满足使用结构f的一种可靠的方法。
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); void main () { struct LIST e={5} ,f={0} ; f=* (Add (d ,e )); printf (\"f.a=%d ,f.b=%dn\" , f.a ,f.b ); printf (\"f.a+f.b=%dn\" , f.a+f.b ); } struct LIST *Add (struct LIST d ,struct LIST e ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+e.a ; p->b=d.b+e.b ; return p ; }