映月读书网 > C语言解惑 > 21.5 修改传递的结构参数的值 >

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=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
;
}