指針與下標計算

差一錯誤是所有程序員的噩夢!

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

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

在《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 ]的地址爲

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章