讀古今文學網 > C語言解惑 > 6.7 函數的返回值 >

6.7 函數的返回值

設計函數一定要明確函數的返回值,只有明確返回值才能確定函數的類型。函數返回值和參數是兩回事。參數是指函數參數表中的變量,這個變量視傳遞方式而變。傳數值不改變參數的值,而傳地址值是改變參數值的必要條件,但是否改變,則視使用方法而定。函數返回值是指被調用函數結束時所返回的值,非void類型的返回值就是return後面表達式的值。返回值的設計就是要保證調用它的函數正確接收這個值。

6.7.1 無返回值的void類型函數

雖然void類型函數不返回值,但並不說明它不能提供中間值。

【例6.9】找出下面程序中的錯誤。


#include <stdio.h>
void max
(int 
, int 
);          //
函數參數採用傳數值方式
int main
()
{
     int a
, b
,c
;
     printf
(\" 
輸入兩個整數:\"
);
     scanf
(\"%d %d\"
,&a
,&b
);
     c=max
(a
,b
);
     return 0
;
}
//
將變量作為參數,以傳數值方式傳遞參數
void max
(int a
, int b
)
{
     if
(a<b
)  printf
(\"
最大值是:%dn\"
,b 
);
     else     printf
(\"
最大值是:%dn\"
,a 
);
}
  

【解答】max沒有返回值,它直接輸出求值結果,不能將它賦值給c。刪除變量c的聲明,然後直接使用語句「max(a,b);」即可。這種調用方式稱為直接調用函數語句。

【例6.10】找出下面程序中的錯誤。


#include <stdio.h>
void max
(int 
, int 
);          //
函數參數採用傳數值方式
int main
()
{
     int a
, b
,c
;
     printf
(\" 
輸入兩個整數:\"
);
     scanf
(\"%d %d\"
,&a
,&b
);
     max
(a
, b
);               //
傳數值
     printf
(\"
最大值是:%dn\"
,c 
);
     return 0
;
}
//
將變量作為參數,以傳數值方式傳遞參數
void max
(int a
, int b
)
{
     if
(a<b
)  c=b
;
     else     c=a
;
}
  

【解答】max函數使用的是主函數里的變量c,這是錯誤的。在主函數之外將c聲明為全局變量即可。這時雖然也是函數語句調用,但被調函數改變全局外部變量的值。一定要注意:void函數沒有返回值,但它可以改變外部變量的值。雖然一般不提倡使用這種方法,但在某種場合下還是很有用的。因為它簡化了函數本身的設計,如果已經存在外部變量,為何不好好利用呢?函數可以使用外部變量,所以可直接將結果賦給外部變量。主函數就可以使用外部變量的值。

注意外部變量名不能與函數的變量名相同。在上例中,如果聲明全局變量c,而沒有去掉主程序的變量c,就會出錯。

【例6.11】為何下面的程序計算的結果不對?


#include <stdio.h>
void max
(int 
, int 
,int
);
int main
()
{
      int a
, b
,c
;
      printf
(\" 
輸入兩個整數:\"
);
      scanf
(\"%d %d\"
,&a
,&b
);
      max
(a
, b
,c
);
      c=a+b+c
;
      printf
(\"c=%dn\"
,c 
);
      return 0
;
}
void max
(int a
, int b
,int p
)
{
      if
(a<b
) p=b
;
      else    p=a
;
}
  

【解答】max的第3個參數要設為指針參數。修改後的程序如下:


#include <stdio.h>
void max
(int 
, int 
,int*
);
int main
()
{
      int a
, b
,c
;
      printf
(\" 
輸入兩個整數:\"
);
      scanf
(\"%d %d\"
,&a
,&b
);
      max
(a
, b
,&c
);
      c=a+b+c
;
      printf
(\"c=%dn\"
,c 
);
      return 0
;
}
void max
(int a
, int b
,int * p
)
{
      if
(a<b
) *p=b
;
      else    *p=a
;
}
  

【例6.12】下面程序在函數原型的聲明中,對數組採用兩種不同的聲明,哪個聲明是正確的?所設計的函數類型是無返回值的void類型,程序想對數組a的元素數值反序,設計為void類型能行嗎?


#include<stdio.h>               //
預編譯命令
void Exch
(int *
);
void Display
(int [ ]
);
int main
()
{
      int a={1
,3
,5
,7
,9}
;
      Display
(a
);
      Exch
(a
);
      Display
(a
);
      return 0
;
}
void Exch
(int a[ ]
)
{
     int c
;
     c=a[0]
;  a[0]=a[4]
;  a[4]=c
;
     c=a[1]
;  a[1]=a[3]
;  a[3]=c
;
}
void Display
(int a[ ]
)
{
     int i
;
     for
( i=0
;i<5
;i++
)
            printf
(\"%d  \"
, a[i]
);
     printf
(\"n\"
);
}
  

【解答】對數組而言,數組名就是首地址的指針,所以兩個格式都是可以的。

void類型的函數是說函數沒有返回值,並不是說void類型的函數不能改變傳遞的參數值。函數的返回值不能是數組,但可以將數組作為參數以傳地址值的方式傳給被調函數,由被調函數通過存儲數組的地址修改數組元素的值。

程序運行結果如下:


1  3  5  7  9
9  7  5  3  1
  

6.7.2 函數返回值問題

1.非void類型的函數必須返回一個值

【例6.13】下面的程序用來改變字符數組的內容。


#include <string.h>
#include <stdio.h>
int st
( char 
);
int main
( 
)
{
     char s=\"Good Afternoon
!\"
;
     printf
(\"%sn\"
, s
);
     st
(s
);
     printf
(\"%sn\"
, s
);
     return 0
;
}
int st
(char s
)
{
     strcpy
(s
,\"How are you
?\"
);     //
改變字符數組內容
}
  

第1次編譯給出警告信息,第2次能產生正確結果。如何排除警告信息?

【解答】st函數沒有返回值。因為主函數沒有使用變量接收函數的返回值,而函數直接修改參數的值,所以只給出警告信息。在st函數增加「return 0;」語句即可排除警告信息。

對於非void類型,即使不使用它的返回值,也必須使用return語句返回一個值。一般返回0值表示正常返回。

其實,本程序不需要返回值,將st函數設計為void類型,即


void st
(char s
)
{ strcpy
(s
,\"How are you
?\"
);}
  

2.函數使用臨時變量作為返回值

【例6.14】試問這個程序正確嗎?


#include <stdio.h>
int max
(int 
, int 
);
int main
()
{
     int a=33
,b=55
,c=0
,d=100
;
     max
(a
, b
);               //6
     d=d+c
;               //7
     printf
(\"c=%dn\"
,d 
);
     return 0
;
}
int max
(int a
, int b
)
{
     int x=5
,y=8
,c
;
     if
(a<b
) c=b+x
;
     else   c=a+y
;
     return c
;
}
  

【解答】程序運行正確,但沒有意義。程序沒有實現任何功能。max使用自己的臨時變量c作為返回值,這個變量與主程序的同名變量沒有關係。主程序沒有接收這個返回值,也沒有使用它,max調用結束,返回值也就失去任何意義。

主程序使用它只能有兩種方式。一是使用一個同類型的變量接收它的返回值。修改語句


c=max
(a
,b
);
  

這時「d=d+c;」就有了意義。二是作為printf函數的參數,刪除6和7兩條語句,使用


printf
(\"c=%dn\"
,d+max
(a
,b
));
  

輸出最終結果。

3.不能使用臨時數組名作為返回值

【例6.15】返回值錯誤的例子。


#include <stdio.h>
int *sp
( int [ ]
);
int main
( 
)
{
      int a[3]={1
,3
,5}
,i
,*p
;
      for
(i=0
; i<3
; i++
)
               printf
(\"%d \"
, a[i]
);
      printf
(\"n\"
);
      p=sp
(a
);
      for
(i=0
; i<3
; i++
)
               printf
(\"%d \"
, *
(p+i
));
      printf
(\"n\"
);
      return 0
;
}
int *sp
(int s
)
{
      int b[3]
;
      b[0]=2+s[0]
;  b[1]=4+s[1]
;  b[2]=6-s[2]+b[1]
;
      return b
;
}
  

數組b是函數sp的臨時數組,函數不能返回數組,這裡其實是返回數組首地址的指針。雖然使用指針把存儲b的首地址返給主函數,但當函數消失後,數組也就不存在了。所以返回的只是一個指向原來存儲b的首地址,因為數組b不存在了,當然這個地址的內容也就不可預測了,所以對這個地址的一系列操作,也就無所適從了。

解決的辦法是使用static定義數組b,使得返回的指針指向靜態數組b。由此可見,並不是使用指針就能保證返回值。被調函數里定義的變量可以作為返回值,但不能使用普通數組。

4.返回臨時指針必須是首地址

【例6.16】下面是將例6.15的sp函數改寫的程序,找出存在的錯誤。


int *sp
(int s
)
{
         int *p
;
         p=
(int*
)(malloc
(3*sizeof
(s
)));
         *p++=2+s[0]
;
         *p++=4+s[1]
;
         *p=6-s[2]+*
(p-1
);
         p=p-1
;
         return p
;
}
  

【解答】程序計算錯誤。*(p-1)是利用偏移量,沒有移動指針的位置,這時的指針是指向第3個元素。返回指針時,一定要保證是分配給指針的首地址。

將「p=p-1;」改為:「p=p-2」即可。

另外,申請內存的數量是sizeof(s)(它計算的是整個數組)。推薦直接使用下標,即


int *sp
(int s
)
{    
          int *p
;
          p=
(int*
)(malloc
(sizeof
(s
)));
          p[0]=2+s[0]
;
          p[1]=4+s[1]
;
          p[2]=6-s[2]+p[1]
;
          return p
;
}
  

每種數據類型都可定義相應的函數類型和指針函數,並在函數里面使用return語句返回一個或多個返回值,但每次調用只有一個滿足返回條件,而且返回值的類型必須與函數類型一致。除非出錯時的強行退出,否則均絕無例外。

void不能定義數據類型,但可以定義函數和指針。由於void類型的函數沒有返回值,所以常用來輸出信息。

如果不允許被調函數修改實參的值,可以使用const限定。