结构可以作为函数的参数,函数可以不返回值(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:11a:8,b:11p=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