當編寫程式時,我們會經常使用到數組,數組是編程經常用到的資料存放區結構。現在,假設我們要使用擁有10個元素的數組,通常情況下我們會面臨著怎麼定義數組?如何訪問數組的元素?如何通過下標訪問數組元素?數組的上界和下屆等問題。
首先來看看Basic語言是如何定義數組的,以定義10個整形元素為例。則數組定義為Dim arr(9) as Integer。arr為數組名,9為數組上界,預設下界為0。如果定位Dim arr(10) as Integer,則arr擁有11個元素。當然Basic同時也支援指定上界和下界的定義形式,如Dim arr(1 to 10) as Integer。
但在主流的C、C++、java語言中,上述這個數組下標只有0到9,不可以指定上界和下界,通常定義如:int arr[10](當然java為 int arr[] = new Int[10])。C、java數組定義需要指定大小,其中arr[10]指定了數組大小為10,但卻沒有下標為10的數組元素。
在通過下標訪問數組時,特別需要注意數組的上界和下界。這裡假設有C程式如:
int i,a[10];
for(i=1;i<=10;i++){
a[i] = 0;
}
請問,這段程式運行結果如何?
相信很多人都會看出,上述程式出現了數組越界的情況。如果是java程式,出現數組越界時,會拋出ArrayIndexOutOfBoundsException異常。經過筆者上機驗證,上述程式在C語言環境下可以順利編譯通過,但遠行的時候卻程式進入了死迴圈。首先C語言不檢查數組越不越界,沒有異常捕獲功能。上述程式中,但變數i自增到10的時候,由於數組a不存在a[10]的元素,但執行a[i] = 0時,實際上是執行了將0負值給i,即i又重新擷取值為0,因此,又可以重新進入到for迴圈,導致程式進入死迴圈。
假設現在將程式改為:
int i,a[10];
for(i=0;i<=12;i++){
a[i] = 15;
}
即便數組確實越界了,但是程式還是可以正常運行,原因在於C程式不對數組越界做檢查。但是,如果在java等其他語言中,這肯定是錯誤的。
因此,在編寫程式時要特別注意邊界問題。
現在假設有整數x滿足X>=16且X<=37,問X可以取多少個數?在第一反應情況下,我們大致會用下界減去上界(37-16=21),因此,X的取值個數大致為21個左右,即20、21或者22個。現在不妨把問題特殊化,假設X滿足X>=1且X<=3,那麼,我們可以列出X取值為1,2,3。顯然X有3個值,同樣X>=16且X<=37中X應該可以取22個,而不是21個。但是如果我們稍微不注意,我們有時候會計算失誤。
如果我們把問題描述為:整數x滿足X>=16且X<38,問X可以取多少個數?顯然答案還是22。但是通過將同一個問題描述成這樣,我們第一感覺用38-16 = 22,很容易就得到了正確答案。
上述所舉得例子,主要想引入“不對稱邊界”思想。
“不對稱邊界”是將“下界上界問題”轉為“入界和出界問題”,將下界作為“入界點”包括在取值範圍之內,而“出界點”在下界之後,不包括在取值範圍內。如X>=16且X<38,入界點“16”包括在取值範圍之內,而“38”是出界點,不包括在取值範圍之內。
在編寫程式時,巧用“不對稱邊界”還是很有用的,比如上述的迴圈例子中,應該寫成
int i,a[10];
for(i=0;i<10;i++){
a[i] = 0;
}
另外,在編寫字元緩衝區程式時,
#define N 1024
static char buffer[N];
static char *bufptr;//指標,用於指向待放入字元的地方
那麼,bufptr到底應該指向最後一個已經佔用的字元,還是讓它指向緩衝區中第一個未被佔用的字元?
這時候使用“不對稱邊界”的慣例,編寫程式時比較靈巧,會帶來許多方便。
比如向緩衝區寫入一個字元c時,可以編寫
*bufptr++ = c,字元c放入bufptr指向的位置後,bufptr遞增1又重新指向緩衝區中第一個未被佔用的字元。
又如當bufptr == &buffer時,表示緩衝區為空白;當(bufptr - bufptr)==N時表示緩衝區已經裝滿。
可見,在處理邊界問題時,使用“不對稱邊界”的思想是有益的。