6.3.4 利用函數返回值傳遞數據
一般來說,函數被調用時都有一個返回值,這也是數據傳遞的一種方式。函數的返回值是通過return語句來實現的。return語句的一般格式為:
return[(表達式)];
其中表達式可以是常量、變量、函數引用、數組元素、地址常量和其他形式的表達式等。沒有函數返回值時,(表達式)可以省略,而寫為:
return;
一個函數可以有多個return語句,即多個出口點。從結構化程序設計的角度來講,一般不提倡多個出口點。return語句的功能有:
?如果函數有返回值時,用該語句實現返回值。
?終止函數執行,使控製返回調用者函數。
如果函數沒有返回值時,該語句可以省略。在省略return語句的情況下,函數執行到最後一個右花括號時結束執行,並返回到調用者函數。例如,符號函數:
sign(x)
float x;
{
if(x>0)
return(1);
else if(x= =0)
return(0);;
else
return(-1);
}
該函數有三個出口點。當x>0時,返回值為1;當x=0時,返回值為0;當x<0時,返回值為-1。又如,計算a的n次方函數:
double power(a,n)
double x;
int n
{
double temp;
for(temp=1;n>0;- -n)
gemp * =x;
return(temp);
}
6.3.5 利用全局變量傳遞數據
全局變量對於程序中的所有函數都是可見的,因此可以利用它來實現函數間的通訊。利用全局變量進行函數間的通訊,不但簡單,而且程序的運行效率高。但是,如果函數間使用過多的全局變量,就增加了函數間的聯係,降低了函數的獨立性。而且,由於各個函數都可以對全局變量進行操作,於是空間出錯,而且,出錯後比較難確定其錯誤發生的位置。所以過多的全局變量不易調試和維護。因此,程序中要有節製地使用全局變量。除了大多數函數都使用的數據用全局變量外,一般都用參數傳遞。下麵是使用全局變量傳遞參數的例子;
int sum;
main()
{
int i,j;
printf("Please enter i and j:");
scanf(" % d % d",&i,&j);
plus(i,j);
printf(" \ n i + j = % d",sum);
}
plus(x,y)
int x,y;
{
sum = x + y;
}
此例中使用了一個全局變量sum,它用於存放兩數之和。它把和從函數plus()中傳遞到函數main()。其某次運行結果如下:
Please enter i and j:9 12<CR>
i+j=21
上麵討論了函數之間的通訊方法。但是,在設計一個通用函數(即能夠被用在各種情況下的函數,或大家共同使用的函數)時,不應該把函數建立在全局變量上,即不應該使用全局變量。函數所需要的數據都應該用參數傳遞。使用參數傳遞,除了有助於函數的能用性之外,還能提高函數代碼的可讀性,減少函數因副作用帶來的錯誤。
6.4 數組與函數
我們知道數組是具有相同數據類型數據的有序集合。如何把數組從調用函數傳遞給被調用函數,並在被調用函數中進行加工處理呢?按數據複製的方式傳遞顯然不合理。因為要把數組的各個元素傳遞給被調用函數,就應該在被調用函數的定義中設置與數組元素個數相同的形式參數。如果數組元素很多時,形參的個數也就很多。這樣既不便使用,執行效率又低。因此,一般都不采用這種方法傳遞。比較合理的方法有:
?在調用函數中,將數組的首地址作為實參來引用函數;而在被調用函數的定義中,用指針型形參來接收它。這樣,一旦把數組的首地址複製給指針變量,被調用函數就可以利用該指針來處理該數組了。
?在調用函數中,將數組首地址作為實參數來調用函數,而在被調用函數的定義中,用數組名作形參,數組的定義隻寫“數組名[]”,而不寫元素個數。利用該數組名接收傳來的數組首地址。於是利用形參中的數組名就可處理傳遞來的數組的諸元素了。
實際上不論被調用函數定義中的形參是指針或數組,實質都是一樣的,是等價的兩種表示形式。下麵是幾個傳遞數組的例子:
【例6-6】編寫程序,求100名職工的平均薪水。
float average(aray) / * 定義函數,數組名作為形參* /
float array[100] / * 形參說明 * /
{
int i;
float aver,sum=array[0];
for(i=1;i<100;i+ +) / * 求薪水總和 * /
sum=sum + array[i];
aver=sum/100 / * 求平均值 * /
return(aver); / * 返回所求的平均值 * /
}
main()
{
float salary[100],aver;
int i;
printf("Please enter 100 salaries: \ n");
for(i=0;i<100;i+ +)
scanf("% f",& salary[i]); / * 輸入職工薪水 * /
printf(" \ n");
aver=average(salary); / * 調用平均值函數 * /
printf("average salary is % 6.3f",aver); / * 輸出結果 * /
}
在程序中,array()為形參數組,salary()為實參數組,salary()內存有100個職工的薪水。
【例6-7】編製程序,求矩陣乘積函數。
matrixmul(x,y,z,m,k,n)
int x[][3],y[][5],z[][5],m,k,n;
{
int i,j,l;
for(i=0;i for(j = 0;j < n;j + +) /* 矩陣y為k行n列 * /
{
z[i][j] =0; / * 矩陣z為m行n列 * /
for(1 =0;1 < k;1 + +)
z[i][j] + =x[i][l] * y[l][j]; / * 矩陣相乘 * /
}
}
main()
{
int a[5][3],b[3][5],c[5][5];
int i ,j;
printf(" \ nPlease enter matrix a: \ n");
for(i =0;i <5;i + +) / * 初始化矩陣a * /
for(i =0;i <3;i + +)
scanf(" % d",& a[i][j]);
printf(" \ n Please enter matrix b:\ n");
for(i =0;i <3;i + +) / * 初始化矩陣b * /
for(i =0;i <5;i + +)
scanf(" % d",& b[i][j]);
matrixmul(a,b,c,5,3,5);
printf(" \ n The product matrix of a and b: \ n");
for(i =0;i <5;i + +) / * 輸入矩陣c * /
{for(j =0;i <5;i + +)
printf(" % d",c[i][j]);
printf(" \ n");
}
}
程序某次運行結果為:
Please enter matrix a:
274<CR>
383<CR>
421<CR>
590<CR>
657<CR>
Please enter matrix b:
17897<CR>
24316<CR>
57422<CR>
The product matrix of a and b:
36 70 53 33 64
34 74 60 41 75
13 43 42 40 42
23 71 67 54 89
51 111 91 73 86
【例6-8】編寫程序,比較數組的大小,假設有數組A和B,若A數組中的元素大於B數組中相應元素的數目,則認為A數組大於B數組,如a[i]>b[i]8次,b[i]> a[i]3次,則A數組大於B數組。
main()
{
int a[10],b[10],i,n=0,m=0,k=0;
printf("enter array a: \ n");
for(i=0;i<10;i + +)
scanf(" % d",&a[i]); / * 輸入數組A * /
printf(" \ n");
printf("enter array b: \ n"); / * 輸出數組A * /
for(i=0;i<10;i + +)
scanf("% d",& b[i]); / * 進行數組元素大小的比較 * /
printf(" \ n");
for(i=0;i<10;i + +)
{
if(large(a[i],b[i])= =1)n =n + 1; / * 數組A大於數組B * /
else if(large(a[i],b[i])= =0)m = m + 1; / * 數組A和B相等 * /
k = k +1; / * 數組A小於數組B * /
}
printf("a[i]>b[i]%d times a[i]=b[i]%times
a[i] ",n,m,k);
/ * 輸出結果 * /
if(n>k)printf("array a is large than array b \ n");
else if(n else
printf("array a is equal to array b \ n");
}
large(x,y) / * 數組比較函數 * /
int x,y;
{
int flag; / * 標記變量 * /
if(x>y)flag=1; / * A >B,flag =1 * /
else if(x else flag =0; / * A=B,flag =1 * /
return (flag);
}
6.5 字符串和函數
在C語言程序中,經常遇到字符串處理。如何將字符串傳遞給函數,進而對其處理呢?其方法類似於對數組的處理。因為C語言是使用字符數組來處理字符串的,所以處理字符串的函數與處理數組的函數在本質上是相同的。字符串傳遞給函數時,一般用地址複製方式把字符串的首地址傳遞給函數,此時函數的形參應是字符指針或字符數組方式。下邊用幾個例子說明函數間如何傳遞字符串。
【例6-9】在字符組中存有一組字符串,將字符串排序,並按ASCII的順序輸出字符串。
main()
{
static char * weekday[] ={"Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Seturday"};
/ * 初始化字符型指針數組 * /
int i;
sortstr(weekday,7); / * 調用排序函數 * /
for(i=0,i<7;i+ +)
printf("% s \ n",weekday[i]); / * 輸出結果 * /
}
sortstr(p,n) / * 排序函數 * /
char * p[];
int n;
{
int i,j,gap; / * 定義整型變量 * /
char * temp; / * 定義字符型指針變量 * /
for(gap= n/2;gap>0;gap / =2) / * 排序過程 * /
for(i=gap;i for(j=i-gap;j >=0;j- =gap)
{
if(strcmp(p[j],p[j +gap])< =0)
break;
temp = p[j];
p[j]=p[j+gap];
p[j+gap]=temp;
}
}
此函數的運行結果如下:
Friday
Monday
Saturday
Sunday
Thursday
Wednesday
在該程序中,sortstr()函數的功能是把多個字符串按字典排序規則由小到大排序。形式參數p作為字符指針數組使用。在主函數中,函數調用語句的實參是字符串指針數組的首地址。
【例6-10】編寫程序,求字符串長度,形參用指針。
stringlenth(str) / * 求字符串長度的函數 * /
char * str
{
int i;
for(i=0; * str ! = ' \ 0 ';s + +)
i + +
return(i);
}
main()
{
char s[100];
int lenth;
printf(" \ nPlease enter a string:");
scanf(" % s",s);
lenth = stringlenth(s);
printf(" \ n The lenth of the first string is % d",lenth);
lenth=stringlenth("This is a c program.");
printf(" \ n The lenth of the second string is % d",lenth);
}
此例中,第一次調用stringlenth(s),實參是字符數組的首地址,第二次調用stringlenth("This is a c program."),實參是字符串首地址。被調用函數的形參是指針。在調用時把字符串首地址傳遞給形參,於是被調用函數就可對其處理。
6.6 指針型函數
前麵幾節主要討論了參數為變量、數組、字符串時函數的定義和引用等問題。本節從返回值方麵討論函數的性質。
6.6.1 指針型函數的定義和引用
在前麵的討論中,我們已經講過,有返回值的函數每次調用時都返回具有某種數據類型的值。其返回值可以是一般數據,也可以是地址量。如果函數的返回值是地址量時,則稱此函數是指針型函數。指針型函數定義一般格式如下:
[存儲類型][數據類型標識符] * 函數名([形參表])
[形參說明;]
{
內部數據說明語句;
執行語句;
}
其中:
存儲類型:是指該函數本身的存儲類型,與一般函數一樣,有外部和static兩種。
指針型函數返回的地址量可以是變量的地址、數組的首地址,指針變量或後麵將要講的結構、聯合等結構的首地址。但不能是其內部定義的自動量或寄存器變量的地址。應該是外部量或靜態量的地址。因為自動變量或寄存器變量在定義它的函數執行結束時,分配給它的存儲空間已釋放,從而其地址也就不存在了,所以不能返回這些量的地址。指針型函數的引用應注意如下幾點:
?同其他函數一樣,指針型函數在引用之前應先定義或說明,什麼條件下應該說明,以及在什麼位置說明相同與其他函數。說明的格式如下:
[存儲類型][數據類型標識符]*函數名();
?在調用指針型函數時,接收返回值的變量一定是與被調用函數數據類型一致的指針,不得使用數組名接收指針型函數的返回值,因為數組名是地址常量,不能進行賦值。
6.6.2 指針型函數的應用舉例
下麵是兩個指針型函數的應用實例,從中可以看出如何定義和引用指針型函數。
【例6-11】編寫用二分法查找字符串的函數。
所謂二分法就是將要查找的對象由小到大排序構成一個有序表。查找時,首先與表中的中間對象進行比較;若相等,則查找,若要查找的對象小於表中的對象,則取表中值較小的一半繼續查找。若要查找的對象大於表中的對象,則取表中值較大的一半繼續查找。如此繼續下去,直至查到或表被查完。每次查找總是選中間元素進行比較。