当编写程序时,我们会经常使用到数组,数组是编程经常用到的数据存储结构。现在,假设我们要使用拥有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时表示缓冲区已经装满。

 

可见,在处理边界问题时,使用“不对称边界”的思想是有益的。

posted on 2012-11-20 22:01  烤德  阅读(923)  评论(0编辑  收藏  举报