静态数组的索引越界问题

1、静态数组

处理静态数组:

#define N 10  //数组的长度N在编译时已知
  T  static_array[N];
这里,数组的长度在编译时是已知的并且不会改变。当然,为了使用具有边界检查的安全数组,也可以vector模板,并在一个构造函数中指定它的长度:

scpp::vector vect(N);
它的效果与静态数组完全相同,但问题在于效率。静态数组是在堆栈上分配内存,而vector模板是在构造函数中用new操作符分配内存的,速度相对慢一些。如果运行时效率至关重要,最好使用array模板,即
namespace scpp
{
//固定长度的数组
	template <typename T, unsigned N>
	class array
	{
	public:
		typedef unsigned size_type;
		//最常用的构造函数
		array() {};
		
		explicit array(const T& initial_value)
		{
			for(size_type i=0; i<size(); ++i)
				data_[i] = initial_value;
		}

		//注意:这里并没有提供拷贝构造函数和赋值操作符
		//依赖的是编译器生成的这些方法的默认版本

		T& operator[] (size_type index)
		{
			SCPP_TEST_ASSERT(index < N,
				"Index "<<index<<" must be less than "<<N);

			return data_[index];
		}

		const T& operator [] (size_type index) const
		{
			SCPP_TEST_ASSERT(index < N,
				"Index "<<index<<" must be less than "<<N);
			return data_[index];
		}

		//模拟迭代器的访问方法
		T* begin() 
		{
			return &data_[0];
		}
		const T* begin()const
		{
			return &data_[N];
		}

	private:
		T data_[N];
	};
}//namespace SCPP
这个数组的行为与C的静态数组完全一样。但是,在编译时激活了表示安全检查的SCPP_TEST_ASSERT宏时,它会提供索引边界检查。它提供了用于模拟迭代器的begin()和end()的方法,因此这种数组在有些场合可以代替vector模板(例如,用于对数的排序)、例如下面的程序使用了STL的sort算法对这个数组进行排序:

#include<algorithm>

 scpp::array<int, 5> a(0);
 a[0] = 7;
 a[1] = 2;
 a[2] = 3;
 a[3] = 9;
 a[4] = 0;

 cout<<"Array before sort: "<<a<<endl;
 sort(a.begin(), a.end());
 cout<<"Array after sort: "<<a<<endl;
 endl;
程序运行后产生的结果输出为:

Array before sort:7 2 3 9 0

Array after sort: 0 2 3 7 9

作为一个附加的优点,我们还可以使用<<操作符,它允许我们像前面那个例子一样把一个数组输出到流中,只要这个数组不至于太大,并且模板类型T定义了<<操作符。当然,这种固定长度的数组在使用时必须限制在数组长度N不会太大的情况下。否则,就会把大量的堆栈内存(一种有限的资源)消耗在这个数组上。

因此,建议不要使用静态或者动态分配数组,而是使用vector或array模板。这种做法可以解决当我们使用了带了方括号的new操作符时,在使用delete操作符时也要带方括号。如果错用了这两个操作符带方括号(new带方括号,而delete没带方括号或者反过来),就会破坏内存堆,一般会导致不良后果。决定不使用动态分配的数组(通过带方括号的new操作符创建)之后,我们就会避免索引越界问题和混用带方括号的操作符问题,这样可以极大减少麻烦。


发布了36 篇原创文章 · 获赞 15 · 访问量 22万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章