例1. 有一个窗框长 1米 60厘米 ,准备安装7根铁栏杆,栏杆的距离是多少厘米?
例2. 在一条长 40米 的马路的一边,从头到尾每隔 5米 种一棵树,一共可以种多少棵树?
本文准备通过以上两个小学问题讨论如下几个问题:
1:C++中数组的下标为何从0开始?
2:for循环中的控制条件应该如何规范,使之准确的控制循环,避免恼人的“差一错误”
3:何谓非对称边界法与对称边界法,以及它们的优缺点。
C++中数组的下标为何从0开始?
请见下图:当你解决上面的问题2后,并且植树后同学A已回学校,但工具未带走,请你帮他带回。他告诉你,我的工具遗忘在公路 20米 处。
你怎么办呢?我想聪明的你应该不会去丈量 20米 吧?既然前面我们已经知道两棵树相聚 5M ,那好
20 / 5 = 4
遗忘的工具确实是在第4棵树处吗?文章开篇引出的问题我想管用了。如果按上图的编号的话,准确地说在第5棵树的地方,计算应该是 20/5+1,举一反三的话, 30米 处应该在30/5+1=7棵树处。这不的确是一种方法。
现在如果我们换个思路呢?改变植物的编号,而不是改变我们的运算,我们看下图:
如果按上图编号的话,那我们就可以简单的计算了,不必再有加一的烦恼。而事实上计算机数组下标从零开始的原因之一也是如此,专业术语是“偏移量”如某数组Array[8],第5个元素的地址为数据所占的长度*5=25,这也是本着效率之上的原则(让我们少做一次加法)。
另一个重要的原因为:
如8位机器能表示的最大无符号整数范围应该是255,而2^16方最大的数的范围是256,又是差一问题?差到哪去了呢,毫无疑问,又是0了。比如我的数组想存放256个元素,那怎么办?在计算机早期,资源是非常珍贵的,0号元素必须用上。
for循环中的控制条件应该如何规范,使之准确的控制循环?
让我们看一个案例:
定义一个10个整形数据的数组,并初始化为0。
- #include <iostream.h>
- #define MAXSIZE 10
- void main()
- {
- int i ;
- int a[MAXSIZE] ;
- for(i=0;i<=MAXSIZE;i++)
- {
- a[i] = 0 ;
- cout<<a[i]<<endl;
- }
- }
运行看看,会发生什么?
出乎意料的这是一个死循环,究其原因,罪魁祸首是数组越界。而越界的结果是将循环控制变量i被改写为了0,使之又反复循环,无法退出。
那我们在程序编写的过程中应该如何去规范地写循环而少出错误呢?下面给大家介绍我收集的一些方法:
一:不对称边界法
先复习下初中数学[a,b),这叫左闭右开。如果我们在循环的过程中遵从左闭右开的原则,越界错误将不会发生。
- for(i=0;i < MAXSIZE;i++)
- {
- a[i] = 0 ;
- cout<<a[i]<<endl;
- }
请再试试运行程序,结果一切正常。上面的程序至少有一下两个优点:
1 容易得出循环次数 终止点-初始点。
2 出循环后循环变量的值为终止点。
二:用不对称边界法修正<严蔚敏 数据结构>上顺序线性表的插入算法
有的朋友会有疑问,不对称边界法真可以做为一个循环条件的准则吗?有没有必须用双闭区间的循环条件的应用,数据结构教材上的算法很多都用的是双闭区间做循环条件。
据我认为,数据结构教材上的循环条件都可以更改来实现这一准则,不但可行,而且更优秀,下面为不熟悉数据结构的朋友附带一个简介,看下图:
图1 图2
上图中水果是顺序摆放的,如果我们想插入苹果但又不破坏原有的结构,那么必须将插入点所在的水果依次后移,以便腾出位置。
严的算法:
- int a[6] = {1,1,1,1,1}; //定义存放水果的容器并放入5个草莓,(1代表草莓,)。
- //在位置1处插入苹果,2代表苹果
- int *q = &a[1] ;
- for(int *p = a[4];p >=q;p--) *(p+1) =*p ; //给苹果挪出位置
- *q = 2 ; //大功告成,插入苹果
我的算法:
- int a[6] = {1,1,1,1,1}; //定义存放水果的容器并放入5个草莓,(1代表草莓,)。
- //在位置1处插入苹果,2代表苹果
- for(int *p = a[5];p > a[1];p--) *p =*(p-1) ; //给苹果挪出位置
- *p = 2 ; //大功告成,插入苹果 出循环后循环变量为终止点
这两算法没有大的区别。但我认为我的算法更为优秀,理由有下:
1 遵从了不对称边界的原则,为循环控制提供了一个准则。(循环次数为 终止点-初始点)
2 相比严的算法少定义个指针变量,或者说少做一次运算(出循环后循环变量为终止点)。
3 为此类寻址提供一个准则,将边界定位到目的地会更易理解。