首页 » C语言解惑 » C语言解惑全文在线阅读

《C语言解惑》9.2 结构作为函数参数及函数的返回值

关灯直达底部

结构可以作为函数的参数,函数可以不返回值(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