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

《C语言解惑》21.5 修改传递的结构参数的值

关灯直达底部

可以将结构作为函数的参数,对结构进行操作。不要觉得要修改结构的值,就一定将结构作为地址值传递,这决定设计与使用函数的具体方法。

【例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=8p->a+p->b=16f.a=4341788,f.b=16f.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=8p->a+p->b=16f.a=8,f.b=8f.a+f.b=16p->a=4345840,p->b=16p->a+p->b=4345856f.a=8,f.b=8f.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;}