假設您要編寫一個對數組進行操做的函數,目的是要此函數返回數組內全部元素的和。假設marbles爲這個int數組的名稱。應該如何來調用這個函數?程序員
一種合乎情理的猜想以下:數組
totao = sum(marbles); // 可能的函數調用函數
那麼原型應該是什麼?數組名同時表明數組首元素的地址,所以實際參數marbles是一個int的地址,應該把它賦給一個類型爲指向int的指針的形式參量:優化
int sum(int *ar); //相就的原型指針
函數sum()從該參數能夠獲得什麼信息呢?它獲得數組首元素的地址,並且知道能夠今後地址找到一個int。請注意它無從知道數組中元素的數量。因而在函數定義中有兩種選擇,第一種是在函數代碼中寫上固定的大小。以下所示:code
int sum(int *ar) //相應定義 { int i; int total = 0; for (i=0;i<10;i++) //假設有10個元素 total+=ar[i]; //ar[i]與*(ar+i)相同 return total; }
上面的代碼利用了這樣的事實:正如能夠在指針符號中使用數組名稱同樣,也能夠數組符號中使用指針。索引
這種函數定義是有限制的,它僅在數組大小爲10時能夠工做。更靈活的方法是把數組大小作爲第二個參數傳遞給函數。原型
int sum(int *ar,int n) //更通用的方法 { int i; int total = 0; for (i=0;i<n;i++) //使用n表示元素的個數 total+=ar[i]; //ar[i]與*(ar+i)相同 return total; }
此外,關於函數參量還有一件須要說明的事情:在函數原型和函數定義的場合中(而且只有在這兩種場合中),可使用int *ar代替int ar[]:編譯器
int sum(int ar[],int n);io
不管在任何狀況下,形式int *ar 都表示ar是指向Int的指針。形式int ar[]也能夠表示ar是指向int的指針,但只是在聲明形式參量時才能夠這樣使用。使用第二種形式能夠提醒讀者ar不只指向一個int數值,並且它指向的這個int是一個數組中的元素。
程序清單10.10 sum_arr1.c程序
//sum_arr1.c --對一個數組的全部元素求和 //若是不能使用%zd,請使用%u或%lu #include <stdio.h> #define SIZE 10 int sum(int ar[],int n); int main(void) { int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20}; long answer; answer = sum(marbles,SIZE); printf("The total number of marbles is %ld.\n",answer); printf("The size of marbles is %lu bytes.\n",sizeof marbles); return 0; } int sum(int ar[],int n) { int i; int total=0; for(i=0;i<n;i++) total+=ar[i]; printf("The size of ar is %lu bytes.\n",sizeof ar); return total; }
輸出結果以下:
The size of ar is 4 bytes. The total number of marbles is 190. The size of marbles is 40 bytes.
請注意marbles的大小爲40字節。的確如此,由於marbles包含10個int 類型的數,每一個數佔4個字節,所以總共40個字節。可是ar只有4個字節。這是由於ar自己並非一個數組,它是一個指向marbles的首元素的指針 。對於採用4字節地址的計算機系統,指針的大小爲4字節(其餘系統中地址的大小可能不是4個字節)。總之,在程序清單10.10中,marbles是一個數組,而ar爲一個指向marbles首元素的指針,C中數組和指針之間的關係容許您在數組符號中使用指針ar。
10.4.1 使用指針參數
使用數組的函數須要知道什麼時候開始和什麼時候結束數組。函數sum()使用一個指針參量來肯定數組的開始點,使用一個整數參量來指明數組的元素個數(指針參量同時肯定了數組中數據的類型)。可是這並非向函數傳遞數組信息的唯一方法。可是這並非向函數傳遞數組信息的唯一方法。另外一種方法是傳遞兩個指針 ,第一個指針指明數組的起始地址(同前面的的方法相同),第二個指針指明數組的結束地址。程序清單10.11中的示例示意了這種方法。這個例子利用了指針參數是變量這一事實,也就是說,程序中沒有使用索引來指示數組中的每一個元素,而是直接修改指針自己,使指針 依次指向各個數組元素。
程序清單10.11 sum_arr2.c程序
//sum_arr1.c --對一個數組的全部元素求和 //若是不能使用%zd,請使用%u或%lu #include <stdio.h> #define SIZE 10 int sump(int * start,int * end); int main(void) { int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20}; long answer; answer = sump(marbles,marbles+SIZE); printf("The total number of marbles is %ld.\n",answer); return 0; } /*使用指針算術*/ int sump(int * start,int * end) { int total=0; while(start < end) { total+=*start; /*把值累加到total上*/ start++; /*把指針向前推動到下一個元素*/ } return total; }
因爲指針start最初指向marbles的首元素,所以執行賦值表達式total+=*start時,把首元素的值 加到total上。而後,表達式start++使指針變量start增1,從而指向數組的下一個元素。start是指向int的指針,所以當start增1時它將增長1個int的大小。
請注意函數sump()和sum()結束加法循環的方式不同。函數sum()使用數組元素的個數作爲第二個參數,循環利用這個值來控制循環次數:
for(i=0;i<n;i++)
而函數sump()則使用第二個指針來控制循環次數:
while(start < end)
由於這是一個不相等關係的判斷,因此處理的最後一個元素將是end所指向的位置以前的元素。這就意味着end實際指向的位置是在數組最後一個元素以後。C保證在爲數組分配存儲空間的時候,指向數組以後 的第一個位置的指針也是合法的。這使上面例子中採用的結構是有效的,由於start在循環中最後獲得的數值是end。請注意使用這種「越界」指針可以使函數調用的形式更加整潔:
sump(marbles,marbles+SIZE);
因爲索引是從0開始的,所以marbles+SIZES指向數組結尾處以後的下一個元素。
順便說一句,儘管C保證指針marbles+SIZES是合法的,但對marbles[SIZE](即該地址存儲的內容)不做任何保證。
能夠把上面的循環體精簡爲一行:
total+=*start++ ;
一元運算符*和++具備相等的優先級,但它在結合時是從右向左進行的。這就意味着++應用於start,而不是應用於*start。也就是說是指針自增1,而不是指針所指向的數據自增1。後綴形式start++而不是++start表示先把指針指向的數據加到total上,而後指針再自增1。若是程序使用++start,則順序就變爲指針先自增1,而後再使用其指向的值。
儘管*start++比較經常使用,但爲了清晰起見,應該使用*(start++)。程序清單10.12中的程序示意了這些在關優先級的微秒之處:
程序清單10.12
/*order.c --指針運算的優先級*/ #include <stdio.h> int data[2]={100,200}; int moredata[2]={300,400}; int main(void) { int *p1,*p2,*p3; p1=p2=data; p3=moredata; printf(" *p1=%d, *p2=%d, *p3=%d\n",*p1,*p2,*p3); printf("*p1++ =%d, *++p2 =%d, (*p3)++ =%d\n",*p1++,*++p2,(*p3)++); printf(" *p1 =%d,*p2 =%d,*p3 =%d\n",*p1,*p2,*p3); return 0; }
輸出結果以下:
*p1=100, *p2=100, *p3=300 *p1++=100,*++p2=200,(*p3)++=300 *p1=200,*p2=200,*p3=301
上面的例子中,只有(*p3)++改變了數組元素的值。其餘兩個操做增長了指針p1和指針p2,使之指向下一個元素。
10.4.2 評論:指針和數組
從前面的介紹中能夠看出,處理數組的函數其實是使用指針作爲參數的。可是在編寫處理數組的函數時,數組符號和指針符號都是能夠選用的。若是使用數組符號,則函數處理數組這一事實更加明顯。也有一些程序員可能更習慣於使用指針,以爲指針使用起來更加天然。
在C中,兩個表達式ar[i]和*(ar+i)的意義是等價的。並且無論ar是一個數組名仍是一個指針變量,這兩個表達式均可以正常工做。然而,只有當ar是一個指針變量時,纔可使用ar++這樣的表達式。
指針符號(尤爲在對其使用增量運算符時)更接近於機器語言,並且某些編譯器在編譯時可以生成效率更高的代碼。然而,不少程序員認爲程序員的主要任務是保證程序的正確性和易讀性,代碼的優化應該交給編譯器去作。