用陣列名做函式引數(轉)
可以用陣列名作函式引數,此時實參與形參都應用陣列名(或指標變數)。
例2:有一個一維陣列score,內放10個學生成績,求平均成績。
float average(float array[10]){
int i;
float aver,sum=array[0];
for(i=1; i<10; i++)sum=sum+array[i];
aver=sum/10;
return aver;
}
main(){
float score[10],aver;
int i;
printf("input 10 scores:/n");
for(i=0; i<10; i++)scanf("%f",&score[i]);
printf("/n");
aver=average(score);//陣列名作為函式引數
printf("average score is %5.2f",aver);
}
說明:
l用陣列名稱作函式引數,應該在主調函式和被調函式分別定義陣列,本例中array是形引數組名,score是實引數組名,分別在其所在的函式中定義,不能只在一方定義。
l實引數組與形引數組型別應該保持一致(這裡都為float型),如不一致,結果將出錯。
l在被呼叫函式中聲明瞭形引數組的大小為10,但在實際上,指定其大小是不起任何作用的,因為C編譯器對形引數組大小不做檢查,只是檢查實引數組的首地址傳給形引數組。因此,score[n]
l形引數組也可以不指定大小,在定義陣列時在陣列名後面跟一個空的方括號,有時為了在被呼叫函式中處理陣列元素的需要,可以另設一個引數,傳遞需要處理的陣列元素的個數,上例可以改寫為下面的形式:
float average(float array[], int n){
int i;
float aver,sum=array[0];
for(i=1; i<n; i++)sum=sum+array[i];
aver=sum/n;
return aver;
}
main(){
float score_1[5]={98.5,97,91.5,60,55};
float score_2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99,5};
printf("the average of class A is %6.2f/n", average(score_1, 5));
printf("the average of class B is %6.2f/n", average(score_2, 10));
}
可以看出,兩次呼叫average函式時,需要處理的陣列元素是不同的,在第一次呼叫時用一個實參5傳遞給形參n,表示求前面5個學生的平均分數。第二次呼叫時,求10個學生平均分。
l最後應該說明一點:用陣列名作為函式實參的時,不是吧陣列元素的值傳遞給形參,而是把實引數組的起始地址傳遞給形引數組,這樣兩個陣列就共佔同一段記憶體單元。見下圖:
起始地址1000 |
2 |
4 |
6 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
b[0]b[1]b[2]b[3]b[4]b[5]b[6]b[7]b[8]b[9]
假如a的起始地址為1000,則b陣列的起始地址也是1000,顯然a和b同佔一段記憶體單元,a[0]和b[0]同佔一個記憶體單元……。由此,我們可以看到,形引數組中各個元素的值如果發生變化會使實引數組元素的值同時發生變化,從上圖是很容易理解的。這一點與變數做函式引數的情況是不同的,務必注意!在程式設計中可以有意識地利用這一點,改變實引數組元素(如排序)。
最後,再給出一個例子:用選擇排序法對陣列中,10個整數按有小到大排序。所謂選擇排序就是先將10個數中最小的數與a[0]對換;再將a[1]到a[9]中最小的數與a[1]對換……,每比較一輪,我們可以找出一個未經排序的數中最小的一個。共比較9論。
a[0]a[1]a[2]a[3]a[4]
36194
16394
1369 4
13496
13469
我們可以看到在執行函式呼叫語句sort(a,10);之前和之後,a陣列各元素的值是不一樣的。原來是無序的,執行sort(a,10)之後是有序的。這也就是所謂的形引數組改變也可以使實引數組隨之改變。
現在,我們已經知道了,當用陣列名作函式引數時,如果形引數組中各元素的值發生了變化,實引數組元素的值也隨之變化。那麼這是為什麼呢?如果你學過指標,這個問題就很容易來回答。
我們先看陣列元素做實參時的情況。如果已經定義了一個函式,其原型為:
Void swap(int x, int y);
假設函式的作用是將兩個形參(x,y)的值進行交換,現在我們這樣呼叫這個函式:
Swap(a[1], a[2]);
用陣列元素a[1], a[2]做實參的情況與變數做實參時一樣,是“值傳遞”方式,我們將a[1], a[2]的值單向傳遞給x,y。當x和y的值改變時a[1], a[2]的值並不改變。
我們現再看用陣列名作函式引數的情況。前面已經介紹,實引數組名代表該陣列首元素的地址。而形參是用來接收從實參傳遞過來的陣列首元素的地址。因此形參應該是一個指標變數。實際上,C編譯器都是將形引數組名作為指標變數來處理的。例如:
我們定義一個被調函式f(int arr[], int n);,實際上,編譯器是把arr按照指標變數來處理的,這相當於將函式f的首地址寫成:f(int *arr,int n)。以上兩種寫法是等價的。
實際上,我們經常用這種方法呼叫一個函式來改變實引數組的值。
這裡,給出一個表格,用於比較變數名作為函式引數和陣列名作為函式引數。
實參型別 |
變數名 |
陣列名 |
要求形參的型別 |
變數名 |
陣列名或指標變數 |
傳遞的資訊 |
變數的值 |
實引數組首元素的地址 |
通過函式呼叫能否改變實參的值 |
不能 |
能 |
注意:
l在用陣列名做函式實參的時候,既然實際上相應的形參是指標變數,為什麼還允許使用形引數組的形式呢?
這是因為在C語言中,用下標法和指標法都可以訪問一個數組,而且用下標法比較直觀,便於理解。因此許多人願意用陣列名做形參,以便與實引數組對應。但從應用的角度講,使用者可以認為有一個形引數組,從實引數組那裡得到起始地址,因此形引數組與實引數組共佔一段記憶體單元,在呼叫函式期間,如果改變了形引數組的值,也就改變了實引數組的值。當然,在主調函式中我們可以使用這些改變了的值。實際上,對C語言比較熟悉的專業人士,往往比較喜歡用指標變數做形參。
l再強調一點:實引數組名代表一個固定的地址,或者說是一個指標型常量,而形引數組並不代表一個固定的地址值。作為指標變數,在函式呼叫開始時,它的值等於實引數組起始地址,但在函式執行期間,它可以再被賦值。例如:
f(arr[], int n){
printf("%d/n", *arr);//輸出array[0]的值
arr=arr+3;
printf("%d/n", *arr);//輸出array[3]的值
}