结构可以作为函数的参数,函数可以不返回值(void类型),也可以返回结构或者结构指针。
【例9.6】下面是把结构d的域值加到f的域值上的例子,分析没有实现预定目标的原因。不修改Add函数的类型,实现程序功能。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST ); void main () { struct LIST f={5} ; Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } void Add (struct LIST d ,struct LIST f ) { f.a=d.a+f.a ; f.b=d.b+f.b ; }
【解答】程序将Add函数的结构f作为传数值的方式传递,当函数返回时,主程序里的参数不会被修改。因为要求不修改Add函数的类型,实现程序功能,所以应该修改参数传递方式,即将传数值改为传地址值(将这个参数以指针方式传递)的方式。
// 修改后的程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; void Add (struct LIST ,struct LIST* ); void main () { struct LIST f={5} ; Add (d ,&f ); // 传结构变量f 的地址 printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } // 将结构f 作为参数,以传地址值的方式传递这个参数 void Add (struct LIST d ,struct LIST *f ) { f->a=d.a+f->a ; f->b=d.b+f->b ; }
因为f.b=0,所以f的f.a=f.b=8。
【例9.7】下面是把一个结构域的值加到另一个结构相应的域值中,编译没有错误,但没有实现预定功能。请分析原因并修改程序。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST Add (struct LIST ,struct LIST ); void main () { struct LIST f={5} ; Add (d ,f ); // 改为f=Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); } // 将结构作为参数,以传数值的方式传递这个参数 struct LIST Add (struct LIST d ,struct LIST f ) { f.a=d.a+f.a ; f.b=d.b+f.b ; return f ; }
【解答】这实际是上一个例子的解决方案。不改变参数传递方式,让Add函数返回结构f,实现对主函数结构f的修改。因为语句“Add(d,f);”中的参数f在调用结束之后就消失了,所以没有改变主程序的f值。应该将Add的返回值接收下来,即用结构f接收返回值。
f=Add (d ,f );
【例9.8】下面是将上例的函数Add改为返回指针的函数,找出存在的问题。
#include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); int main () { struct LIST f={5 ,3} ,*p=&f ; p=Add (d ,f ); printf (\"a=%d ,b=%dn\" , f.a ,f.b ); return 0 ; } // 将结构作为参数,以传数值的方式传递这个参数 struct LIST *Add (struct LIST d ,struct LIST f ) { struct LIST *p=&f ; f.a=d.a+f.a ; f.b=d.b+f.b ; return p ; }
【解答】这个程序的错误很典型。在Add函数里定义的指针变量是自己局部所有的变量,在Add里,p->a=8,p->b=11,p有自己的指向地址。在退出Add函数之后,这个局部变量p消失,主函数里仍然是原来的p,“p=Add(d,f);”只是将主程序里p的指向改变为在Add函数里指向的地址,但是&f没有变化,仍然是原来的值,而Add传递的参数是f的值,所以不改变主程序里的值。但这时p的指向地址里是不可预测的值。
不要以为使用
f=*p ;
语句就能使f的内容与Add返回值一样。其实*p是不确定的值,只有p是确定的值时,才可以使用这种方法。下面的程序就是演示了这个问题。
如上所示,在Add函数里设计的局部指针变量p是使用参数f初始化,一旦退出程序就消失了。其实这是个不稳定因素,容易产生怪七怪八的错误。稳妥的设计是申请一块内存。为了便于理解,下面的程序输出它们的地址和数值供比较。
#include <stdio.h> #include <stdlib.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST ); int main () { struct LIST f={5 ,3} ,*p=&f ; printf (\" 系统分配p=0x%x ,&f=0x%xn\" , p ,&f ); p=Add (d ,f ); printf (\" 调用后p->a :%d ,p->b :%dn\" , p->a ,p->b ); printf (\" 调用后a :%d ,b :%dn\" , f.a ,f.b ); printf (\" 调用后p=0x%x ,&f=0x%dn\" , p ,&f ); f=*p ; printf (\" 执行f=*p ;语句的结果如下。n\" ); printf (\"p->a :%d ,p->b :%dn\" , p->a ,p->b ); printf (\"a :%d ,b :%dn\" , f.a ,f.b ); printf (\"p=0x%x ,&f=0x%xn\" , p ,&f ); return 0 ; } struct LIST *Add (struct LIST d ,struct LIST f ) { struct LIST *p ; p= (struct LIST * )malloc (sizeof (struct LIST )); p->a=d.a+f.a ; p->b=d.b+f.b ; printf (\" 在Add 函数中p=0x%xn\" , p ); return p ; }
程序运行结果如下。
系统分配p=0x12ff78 ,&f=0x12ff78 在Add 函数中p=0x4300c0 调用后p->a :8 ,p->b :11 调用后a :5 ,b :3 调用后p=0x4300c0 ,&f=0x1245048 执行f=*p ;语句的结果如下。 p->a :8 ,p->b :11 a :8 ,b :11 p=0x4300c0 ,&f=0x12ff78
由此可见,当要返回指针时,在被调函数里申请动态内存,不如直接使用一个指针参数。因为&f就是地址,所以在主程序里根本不需要再设计指针,只要将Add函数传递的f改为传递指针即可,这样一来,程序就变得非常简单了。
// 将f 作为指针传递给Add 函数的源程序 #include <stdio.h> struct LIST{ int a ,b ; }d={3 ,8} ; struct LIST *Add (struct LIST ,struct LIST* ); int main () { struct LIST f={5 ,3} ; Add (d ,&f ); printf (\"a :%d ,b :%dn\" , f.a ,f.b ); return 0 ; } struct LIST *Add (struct LIST d ,struct LIST *f ) { f->a=d.a+f->a ; f->b=d.b+f->b ; return f ; }
显然,Add函数可以简化为void类型的函数。
【例9.9】改正如下程序中的错误。
#include <stdio.h> #include <stdlib.h> typedef struct student { char name[10] ; int studnem ; } *STUDNT ; int main () { STUDNT PT ; if ((PT= (STUDNT ) malloc (sizeof (STUDNT )) ) == NULL ) return 1 ; printf (\" 输入姓名和学号:\" ); scanf (\"%s%d\" , PT->name ,PT->studnem ); printf (\" 姓名:%s 学号:%dn\" , PT->name ,PT->studnem ); return 0 ; }
【解答】这里的STUDNT是定义的新结构类型指针,所以用它定义的PT是结构指针变量,由于sizeof()要求的是结构类型,所以不能再使用sizeof(STUDNT)而使用sizeof(struct student)。同理,malloc()前面不是使用(STUDNT*)而应使用(STUDNT)。
scanf要求的是地址,name是字符串,可以不用加&,但studnem是整数类型,所以必须冠以地址符号&。
// 修改后的程序 #include <stdio.h> #include <stdlib.h> typedef struct student { char name[10] ; int studnem ; } *STUDNT ; int main () { STUDNT PT ; if (( PT= (STUDNT ) malloc (sizeof (struct student )) ) == NULL ) return 1 ; printf (\" 输入姓名和学号:\" ); scanf (\"%s%d\" , PT->name ,&PT->studnem ); printf (\" 姓名:%s 学号:%dn\" , PT->name ,PT->studnem ); return 0 ; }
程序运行示范如下。
输入姓名和学号: 王银英 20983 姓名:王银英 学号:20983