讀古今文學網 > C語言解惑 > 9.2 結構作為函數參數及函數的返回值 >

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
:11
a
:8
,b
:11
p=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