C語言中的邊界計算與不對稱邊界(二)

       儘管C語言的數組會讓新手感到麻煩,然而C語言中數組的這種特別的設計正是其最大優勢所在。要理解這一點,以下是一些簡單解釋。

       在所有常見的程序設計錯誤中,最難於察覺的一類是“欄杆錯誤”,也常被稱爲“差一錯誤”(off-by-one error)。例如這個問題:100英尺長的圍欄每隔10英尺需要一根支撐用的欄杆,一共需要多少根欄杆呢?如果不加思索,大家會容易以爲是100除以10,即爲10根。其實,真正需要的是11根。

       因爲支撐10英尺長的圍欄實際需要2根欄杆,即兩端各一根。這個問題的另一種思考方式是,除了最右側的一段圍欄,其他每一段10英尺長的圍欄都只在左側有一根欄杆;而例外的最右側一段圍欄不僅左側有一根欄杆,右側也有一根欄杆。

       爲了避免以上問題中所說的“欄杆錯誤”,以下是兩個注意的通用原則:

     (1)首先考慮最簡單情況下的特例,然後將得到的結果外推;

     (2)仔細計算邊界,絕不掉以輕心;

      將上面這兩點牢記住以後,現在看看整數範圍的計算。例如,假定整數x滿足邊界條件x>=16且x<=37,那麼此範圍內的X的可能取值個數有多少?即16到37之間一共有多少個元素?很顯然,答案與37-16(21)非常接近,那麼到底是20、21還是22呢?

      根據原則1,我們考慮最簡單情況下的特例。這裏假定整數x的取值範圍上界與下界重合,即x>=16且x<=16,顯然合理的x取值是隻有一個整數,即16。所以當上界與下界重合時,此範圍內滿足條件的整數序列只有一個元素。

      再考慮一般情況,假定下界爲l,上界爲h。如果滿足條件“上界與下界重合”,即l=h,則h-l=0。根據特例外推的原則,我們可以得出滿足條件的整數序列有h-l+1個元素。故以上的問題中的x的取值個數爲37-16+1=22。

      造成“欄杆錯誤”的根源正是“h-l+1”中的“+1”。一個字符串中由下標爲16到下標爲37的字符元素所組成的子串,它的長度是多少呢?稍不留意,就會得到錯誤的結果21。很自然的,我們會想,是否存在一些編程技巧,能夠降低這類錯誤發生的可能性呢?

       當然,這個技巧不但存在,而且還可以一言以蔽之:用第一個入界點和第一個出界點來表示一個數值範圍。具體而言,前面的例子我們不應該說整數x滿足邊界條件x>=16且x<=37,而是說整數x滿足邊界條件x>=16且x<38。注意,這裏下界是“入界點”,即包括在取值範圍之中;而上界是“出界點”,即不包括在取值範圍之中。這種不對稱也許從數學上而言並不優美,但是它對於程序設計的簡化效果卻足以令人吃驚。

   (1)取值範圍的大小就是上界與下界之差。

   (2)如果取值範圍爲空,那麼上界等於下界

   (3)即使取值範圍爲空,上界也永遠不可能小於下界。

       對於像C這樣的數組下標從0開始的語言,不對稱邊界給程序設計帶來的便利尤爲明顯;這種數組的上界恰是數組元素的個數。因此,在C語言中定義一個擁有10個元素的數組,那麼0就是數組下標的第一個“入界點”,而10就是數組下標中的第一個“出界點”。因而我們經常這樣寫

int a[10], i;
for(i=0; i<10; i++)
       a[i] = 0;
而不是這樣寫:

int a[10], i;
for(i=0; i<=9; i++)
       a[i] = 0;
       讓我們做一個假設,如果C語言的for語句風格類似Algol或者Pascal語言,那麼就會帶來一個問題:下面這個語句的含義究竟是什麼?

for(i=0 to 10)
      a[i] = 0;
       如果10是包括在取值範圍內的“入界點”,那麼i將取11個值,而不是10個值;如果10是不包括在取值範圍內的“出界點”,那麼原來以其他程序語言爲背景的編程者會大爲驚訝。

        另一種考慮不對稱邊界的方式是,把上界視作某序列中第一個被佔用的元素,而把下界視作序列中第一個被釋放的元素。如下圖:







發佈了36 篇原創文章 · 獲贊 15 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章