1、在C++中,可以采用几种不同的方法创建一个某种类型T的对象的数组。3种常用的方法如下:

#define N 10 //数组的长度N在编译时已知T static_array[10];int n = 20; //数组的长度n是在运行时计算的T* dynamic_array = new T[n];std::vector<T> vector_array; //数组的长度可以在运行时进行修改

当然,我们仍然可以使用calloc()和malloc()函数,并且这样的程序仍然能够通过编译并顺利运行。但是,混合C和C++代码并不是良好的编程思路,除非由于依赖遗留的C函数库的原因而必须这样做。不管我们用什么方法分配数组,都可以用一个无符号整数作为索引访问数组中的元素。

const T& element_of_static_array = static_array[index];
const T& element_of_dynamic_array = dynamic_array[index];
const T& element_of_vector_array = vector_array[index];

如果我们提供一个大于或等于数组长度的索引值,会发生什么情况呢?以上的代码都会安静的返回垃圾数据。如果我们决定在赋值符的左边使用[]操作符,情况就会变得更糟。

some_array[index] = x;

取决于运气,这个操作可能会重写其他某个不相关的变量,一个其他数组的元素甚至是一条程序指令。在最后一种情况下,程序很可能会崩溃。每种错误都向恶意入侵者提供机会接管程序并产生不良后果。但是,std::vector提供了一个at(index)函数,它通过抛出一个out_of_range异常执行边界检查。它的问题在于如果我们想执行这种安全检查,必须在访问数组元素的每个地方都严格地使用at()函数。显然,这种做法会降低代码的效率。因此在完成了测试之后,我们可能想用速度更快的[]操作符在每处对它进行替换。但是,这种替换需要对代码进行大量的修改,工作量很大,并且在此之后还要对代码进行重新测试,因为在这个乏味的过程中,很可能会不小心输错数据。

因此较之使用at()函数,使用以下的方法。尽管动态数组使[]操作符完全超出自己的控制,但STL的vector容器把它实现为一个C++函数,我们可以根据自己的缺陷捕捉目标对它进行改写,例如,重新定义[]操作符:

T& operator [](size_type index)
{SCPP_TEST_ASSERT(index < std::vector<T>::size(),"Index "<<index<<" must be less than "<<std::vector<T>::size());return std::vector<T>::operator[](index);
}const T& operator [](size_type index) const
{SCPP_TEST_ASSERT(index < std::vector<T>::size(),"Index "<<index<<" must be less than "<<std::vector<T>::size());return std::vector<T>::operator[](index);
}

使用上面定义的文件中的代码,举例如下:

#include <iostream>
#include "scpp_vector.hpp"using namespace std;int main()
{scpp::vector<int> vect;for(int i=0; i<3; i++)vect.push_back(i);cout<<"My vector = "<<vect<<endl;for(int i=0; i<=vect.size(); i++)cout<<"Value of vector at "<<i<<" is "<<vect[i]<<endl;return 0;
}

首先,我们并没有采用std::vector<int>或简单的vector<int>这样的写法,而是采用了scpp::vector<int>的形式。这是为了把我们的vector与STL的vector区分开来。通过使用scpp::vector,我们把标准实现替换为我们自己的安全实现。scpp::vector还提供了一个<<操作符,因此只要vector不要太大,并且类型T定义了<<操作符,就可以用这个操作符打印vector。

在第二个for循环中,我们没有使用i<vect.size()的写法,而是写成了i<=vect.size()。这是一个极为常见的编程错误,我们只是为了观察当索引越界时会出现什么情况,其实,程序的结果输出为:

My vector = 0 1 2Value of vector at 0 is 0Value of vector at 1 Value of vector at 2 is 2Index 3 must be less than 3 in file scpp_vector.hpp

只要SCPP_TEST_ASSERT_ON符号已被定义,这个安全检查就会起作用,并可以方便的根据需要在编译时打开或关闭这个检查。这种方法的问题是vector的[]操作符在循环内部的使用极为频繁,因此这种安全检查会被大量使用,因为会显著地降低程序的执行速度,就像使用at()函数一样。如果觉得这种方法不适合自己的程序,可以定义一个新宏,例如SCPP_TEST_ASSERT_INDEX_OUT_OF_BOUNDS,它的工作方式与SCPP_TEST_ASSERT完全相同,但只在scpp::vector::operator[]内部使用。SCPP_TEST_ASSERT_OUT_OF_BOUNDS与SCPP_TEST_ASSERT的区别应该是它的打开或关闭与SCPP_TEST_ASSERT宏无关,因此如果确信代码没有缺陷,就可以使它处于未激活状态,同时又让其他的安全检查继续有效。

除了允许捕捉索引越界错误之外,vector模板较之静态分配的数组和动态分配的数组还有一个优点,即它的长度可以根据需要增长(只要内存没有耗尽)。但是,这个优点是要付出代价的。如果一个vector预先并未声明需要多少内存,它就会分配一些默认内存(称为它的“容量”)。当这个vector的实际大小达到了这个容量时,它将分配一块更大的内存,把旧数据复制到这块新的内存区域,并用它替换旧的那块内存。因此,随着时间的推移,向vector模板添加一个新元素可能突然变得非常缓慢。因此,如果预先知道需要多少元素,应该像静态数组和动态数组一样,在构造函数中告诉vector需要多少内存:

scpp::vector<int> vect(n);

这样就创建了一个具有指定元素数量的vector,其实我们还可以写成如下:

scpp::vector<int> vect(n, 0);

它还把所有的元素初始化为一个指定的值。

另一种替代方法是创建一个0个元素的vector,但是指定它所需要的容量。

scpp::vector<int> vect;
vect.reserve(n);

这个例子和前一个例子的区别在于,这个例子中的vector是空的(即vector.size()的返回值是0),但是当我们开始向它添加元素时,在它的长度达到n之前,不会出现导致速度降低的容量增长现象。

动态数组索引越界问题相关推荐

  1. 常见问题_数组索引越界异常

    package com.learn.day05.demo03;/* 数组的索引编号从0开始,一直到"数组的长度-1"为止.如果访问数组元素的时候,索引编号并不存在,那么将会发生 数 ...

  2. 数组索引越界异常 ArrayIndexOutOfBoundsException

    如果访问数组元素的时候,索引编号并不存在,那么将会发生 数组索引越界异常 ArrayIndexOutOfBoundsException 原因:索引编号写错了. 解决:修改成为存在的正确索引编号. 所有 ...

  3. java数组索引越界异常如何解决_java之ArrayIndexOutOfBoundsException数组越界与IndexOutOfBoundsException索引越界之间关系...

    ArrayIndexOutOfBoundsException与IndexOutOfBoundsException之间的关系是继承关系,看源代码就可以知道: public class ArrayInde ...

  4. 数组遍历产生的索引越界问题

    当要删除数组指定位置的元素e时 可以使用如下方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ...

  5. java 索引数组_Java的数组索引问题

    /* 数组操作的两个常见小问题: ArrayIndexOutOfBoundsException:数组索引越界异常 原因:你访问了不存在的索引. NullPointerException:空指针异常 原 ...

  6. JAVA基础(索引越界and空指针)

    1,空指针和索引越界 ArrayIndexOutOfBoundsException:数组索引越界异常 原因:你访问了不存在的索引. b:NullPointerException:空指针异常 原因:数组 ...

  7. 基础第一阶段day05——索引越界异常、空指针异常

    ArrayIndexOutOfBoundsException 数组越界异常 public class Test01 {public static void main(String[] args) {i ...

  8. 以太坊EVM动态数组越界导致OOM分析

    solidity 动态数组原理分析 solidity 和Python ,JavaScript 一样,支持动态数组.我们知道,在Python 和JavaScript 里,动态数组内部的对象(比如:Len ...

  9. 数组索引必须为正整数或逻辑值_Office 365函数新世界——动态数组

    革新一个旧的函数体系,无外乎从这么几个方面入手,函数的运行效率.函数的编写方式以及扩展新的函数功能.今天咱们就学习一下,看看365新函数是如何通过这三个方面打破旧函数条条框框的. 365函数系列推出了 ...

  10. vector 容器 动态数组总结

    vector 容器 动态数组总结 二话不说直接上代码 #include <vector> #include <algorithm> #include <iostream& ...

最新文章

  1. 在Andoird studio 中用代码实现setId报错,而在ecplise中可以,的处理方法
  2. netstat获取本机监听的地址列表 —— *awk与splite命令实现*(sudo netstat -nl | grep tcp……)
  3. 十二、Linux系统编程中man命令的使用
  4. 第19讲:Pyppeteer 爬取实战
  5. 注解不自动导包_玩转SpringBoot2.X:SpringBoot自动配置原理大揭秘
  6. 数据同步云端本地_如何从云端删除Windows 8的同步数据
  7. Kafka ACL使用实战
  8. 红帆参加HiMSS 2010(2010亚太区卫生信息大会)
  9. 各种分类算法的优缺点
  10. 【论文写作】如何写出一篇好的学术论文?
  11. Android申请权限(相机权限和读写权限)
  12. 公司邮箱怎么申请注册?如何登录公司邮箱?
  13. 分享| 电脑设置双屏或多屏显示2种简单方法,你能学会几种?
  14. Linux下挂载大容量存储
  15. win7浏览器主页修改不过来_win7浏览器主页不能修改怎么办 win7浏览器主页不能修改解决方法...
  16. 自学 9个月 Java 找到了一份 12K 的工作,前辈的方式值得分享给大伙
  17. linux下apache 的安装,php安装过程
  18. 高并发读,高并发写解决方案
  19. MySQL怎么添加字段在指定列位置、怎么一次添加多个字段?
  20. 华为ensp 错误代码 :40

热门文章

  1. 电子计算机X线体层摄影,电子计算机X线体层摄影检查诊断乳腺肿块的价值
  2. Keil下载代码闪退的问题(已解决)
  3. 奈学教育CEO孙玄:成为一个有情怀的工程师,我的12点思考
  4. STM32F205通过SDIO和SPI读写SD卡文件
  5. 用matlab化简三角函数方程组,matlab化简三角函数 matlab怎么样化简三角函数?
  6. c语言 圆周率10000位,计算圆周率 Pi (π)值, 精确到小数点后 10000 位(C语言)
  7. android Paddle 视频字幕识别TTS语音
  8. 51汇编——矩阵键盘
  9. 蓝桥杯备赛第一天-138译码器
  10. 单片机原理与应用技术