設計函數一定要明確函數的返回值,只有明確返回值才能確定函數的類型。函數返回值和參數是兩回事。參數是指函數參數表中的變量,這個變量視傳遞方式而變。傳數值不改變參數的值,而傳地址值是改變參數值的必要條件,但是否改變,則視使用方法而定。函數返回值是指被調用函數結束時所返回的值,非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限定。