讀古今文學網 > 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
;
}