1. 程式人生 > >指標與下標計算

指標與下標計算

差一錯誤是所有程式設計師的噩夢!

它看起來是一個瑣碎的問題,卻非常煩人,許多程式設計師對它是採取一種“輕蔑”的態度,即使被它虐了千百遍,還是不願意正視這個問題。

其實,差一問題並不是一個小問題,我們應該對它給予足夠的重視。

在《C陷阱與缺陷》中,對這個問題有詳細的討論,這裡結合我的理解寫一寫解決這個問題的方法。

許多程式設計師在遇到差一問題時,常常採用的是“探測法”,一個位置應該寫n還是應該寫n-1,測試兩次就夠了,如果是兩個位置,就要測試四次,三個位置,要測試八次。。。

其中還可能存在“重疊錯誤”,當有兩個錯誤時,測試是正常的,當改正了一個錯誤,反而測試出錯。

一次過寫一段正確的程式碼,總比飛快得寫一堆糟糕程式碼,再做一天debug

好得多。

正確的指標和下標,是計算出來的,不是亂蒙出來的!

指標運算

在學會計算指標之前,先要正確理解陣列和指標。

首先,地址就是一個整數,不是什麼神奇的東西!

指標是地址變數,陣列名是地址常量。

舉個例子,

int arr[10];

編譯器會分配10個整型大小的空間,首地址是arr,假設arr=500,那麼這個陣列就是這樣的:

 

其中能索引到的元素是arr[0]arr[9],arr[n]也就是arr[10]是沒有的。

arr[0]有,arr[n]沒有,這個叫“不對稱邊界”,它帶來極大便利的同時,也引起了許多錯誤,不過,這種設計絕對是正確的。

可以用arr+5,表示第5個元素的地址(從0開始),但是不允許

arr=arr+5,因為陣列名是個常量。這裡arr=500,arr+5不等於505,而是等於520,因為在對地址量做加減時,它自動乘了一個sizeof(int)

指標也一樣,不同的是指標可以賦值。

地址量可以進行下列操作(p表示指標,i表示整型):

p=p+i;    //指標後移i個位置

p=p-i; //指標前移i個位置

i=p1-p2; //兩個指標之間的元素個數

要指向p1p2中間那個元素,可以用p0=p1+(p2-p1)/2,

p0=(p1+p2)/2似乎也可以,不過編譯器不允許,因為p1+p2沒有意義。

問題解決

C陷阱與缺陷》中,差一問題的解決方法,

一個是理解“不對稱邊界”,

另一個是“邊界計算”。

邊界計算,簡單舉個例子。

在一個位置,不知道應該寫n還是n-1,於是可以假設當n=2時,這裡應該寫1,所以,這個位置應該寫n-1

“從110有幾個數?!”

11個!”

“你給我想清楚!”

11個!”

“那從12有幾個數?”

就是這樣了。

解決差一問題的方法絕不止這兩種,我們可以在實際問題的過程中,想到各種不同的方法。

二分查詢

下面以二分查詢為例,講解這個問題。

根據《程式設計珠璣》裡的資料,在100個專業程式設計師中,90%的程式設計師寫的二分查詢是存在bug的。

在沒有bug的二分查詢程式碼中,也可能存在元素的重複比較。

我寫的二分查詢是這樣的:

int* bs(int* arr,int n,int x){

⓪ if(n==0)return NULL;

① else if(x==arr[n/2])return arr+n/2;

② else if(x<arr[n/2])return bs(arr,n/2,x);

③ else return bs(arr+n/2+1,n-n/2-1,x);

}

元素按從小到大的順序排,如果找到,返回的是元素的地址,找不到則返回NULL。(如果找到了bug,請告訴我)。

我寫這段程式碼的過程是這樣的:

先在紙上畫出記憶體模型

  

⓪語句,當陣列沒有元素了,就返回NULL

①語句,比較x是否等於arr[n/2],(這裡arr[n/2]是不是正中間的元素並不重要),如果等於,返回它的地址,arr+n/2

②語句,比較x是否小於arr[n/2],如果小於,應該繼續查詢的是位於arr[n/2]前面的那一段陣列,不包括arr[n/2],因此,這段陣列的首地址還是arr,它的最後一個元素是arr[n/2-1],長度是n/2

③語句,應該繼續查詢的是位於arr[n/2]後面的那一段陣列,不包括arr[n/2],因此,這段陣列的首地址是arr+n/2+1,那麼長度應該怎麼計算呢?

1個方法:右邊陣列長度=原陣列長度-左邊陣列長度-arr[n/2]這個元素

=n-n/2-1

2個方法:右邊陣列的末地址=原陣列的末地址

右邊陣列的首地址+長度=原陣列的首地址+長度

arr+n/2+1+m=arr+n

m=n-n/2-1

所以,右邊陣列的長度為n-n/2-1n-n/2不一定等於n/2,因為整數除法是向下取整的)

待定係數法

我想到的另一個方法,是待定係數法。

以對稱矩陣的壓縮儲存為例,講解這個方法。

對稱矩陣的壓縮儲存是資料結構考試中的必備題目,在各大考試中頻繁露面。

在不同的試題中,具體細節也不同,有的儲存上三角,有的儲存下三角,有的從0開始,有的從1開始,還有些是01混合。

上試題:

一個10階對稱矩陣A,採用行優先順序壓縮儲存下三角元素a[1,1]為第一個元素,其儲存地址為陣列B[0],每個元素佔有1個儲存地址空間,則a[ i, j ]的地址為_________

解法:

先畫出矩陣的一部分


B[t]對應a[ i, j ],即對應方程為,選出四個元素

i=1,j=1時,t=0

i=2,j=1時,t=1

i=3,j=1時,t=3

i=3,j=2時,t=4

 

寫成矩陣形式可以更好計算:


a[ i, j ]的地址為

這裡需要注意的是,選取元素時不能取同一條直線上的元素,否則在計算時矩陣不滿秩,得不到最後結果。