C Primer Plus 第10章 數組和指針 10.4 函數、數組和指針

假設您要編寫一個對數組進行操做的函數,目的是要此函數返回數組內全部元素的和。假設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++這樣的表達式。

指針符號(尤爲在對其使用增量運算符時)更接近於機器語言,並且某些編譯器在編譯時可以生成效率更高的代碼。然而,不少程序員認爲程序員的主要任務是保證程序的正確性和易讀性,代碼的優化應該交給編譯器去作。

相關文章
相關標籤/搜索