动态数组索引越界问题

动态数组索引越界问题1 在 C 中 可以采用几种不同的方法创建一个某种类型 T 的对象的数组 3 种常用的方法如下 defineN10 数组的长度 N 在编译时已知 Tstatic array 10 intn 20 数组的长度 n 是在运行时计算的 T dynamic array newT n std vectorvector array 数组的长度可以在运

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

#define N 10 //数组的长度N在编译时已知 T static_array[10]; int n = 20; //数组的长度n是在运行时计算的 T* dynamic_array = new T[n]; std::vector 
  
    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 
    
      ::size(), "Index "< 
     
       ::size()); return std::vector 
      
        ::operator[](index); } const T& operator [](size_type index) const { SCPP_TEST_ASSERT(index < std::vector 
       
         ::size(), "Index "< 
        
          ::size()); return std::vector 
         
           ::operator[](index); } 
          
         
        
       
      
    

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

#include 
     
       #include "scpp_vector.hpp" using namespace std; int main() { scpp::vector 
      
        vect; for(int i=0; i<3; i++) vect.push_back(i); cout<<"My vector = "< 
        
       
     

首先,我们并没有采用std::vector

或简单的vector

这样的写法,而是采用了scpp::vector

的形式。这是为了把我们的vector与STL的vector区分开来。通过使用scpp::vector,我们把标准实现替换为我们自己的安全实现。scpp::vector还提供了一个< <操作符,因此只要vector不要太大,并且类型t定义了<<操作符,就可以用这个操作符打印vector。< span="">

在第二个for循环中,我们没有使用i

i<=vect.size()。这是一个极为常见的编程错误,我们只是为了观察当索引越界时会出现什么情况,其实,程序的结果输出为:




My vector = 0 1 2 Value of vector at 0 is 0 Value of vector at 1 Value of vector at 2 is 2 Index 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 
     
       vect(n); 
     

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

scpp::vector 
     
       vect(n, 0); 
     

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

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

scpp::vector 
     
       vect; vect.reserve(n); 
     

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

















版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/221841.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午5:10
下一篇 2026年3月17日 下午5:10


相关推荐

  • 元胞自动机模型01——认识元细胞机模型

    元胞自动机模型01——认识元细胞机模型笔记来自 传染病传播模型研究 余雷 nbsp nbsp nbsp nbsp 元胞自动机模型是网络动力学模型中最常用的一种 网络动力学模型主要包括元胞自动机 布尔网络 神经网络以及 L 系统等 元胞自动机 是一种时间和空间都离散的动力模型 虽然从 20 世纪 40 年代初冯诺依曼就提出了这种思想 但是受制于当时的计算机能力 直到 20 世纪 80 年代 Wolfram 才进行了全面的研究 nbsp

    2026年3月17日
    2
  • poe交换机可以当普通交换机吗?-芝士回答_poe交换机和poe交换机对接

    poe交换机可以当普通交换机吗?-芝士回答_poe交换机和poe交换机对接PoE交换机是一种新型的多功能交换机,很多人认为PoE交换机可以自行发电,这种说法并不正确,通常所讲的PoE交换机供电指的是PoE交换机通过网线来给其他设备供电,与此同时也不丢失传输数据的功能。那么,PoE交换机和普通交换机又有什么区别呢?可以将PoE交换机当做普通的交换机来使用吗?本文将为您揭晓这两个问题的答案。PoE交换机和普通交换机的区别PoE交换机和普通交换机都是可以传输数据的网络设备,但两者之间又有什么差别呢?下文会将PoE交换机和普通交换机进行对比,以便能够帮助您更加深入的了解二者之间的

    2022年10月5日
    3
  • 一文彻底搞懂加密、数字签名和数字证书,看不懂你打我!

    一文彻底搞懂加密、数字签名和数字证书,看不懂你打我!前言这本是 2020 年一个平平无奇的周末 小北在家里刷着 B 站 看着喜欢的 up 主视频 在一旁玩手机的女朋友突然问 你知道数字证书是来干啥的不 为啥浏览器提示证书不可信 你要说这个 那我可来劲了 于是乎从加密 数字签名一直讲到了数字证书 终于把女朋友讲睡着了 独自写下这篇文章 正文如果你能非常清晰的回答出以下问题 可以直接拉到最下面帮我点个赞 把时间用去陪陪女朋友 非对称加密中公私钥都可以加密 那么什么时候用公钥加密 什么时候用私钥 加密 什么是数字签名 数字签名的作

    2026年3月18日
    2
  • embedding实现_embedded option

    embedding实现_embedded option假设词汇量为100万个时的CBOW模型如下,输入层和输出层存在100万个神经元。下面两个问题导致耗时严重。问题一、输入层的one-hot表示和权重矩阵的乘积。one-hot表示占用内存过多,计算one-hot表示与权重矩阵的乘积,需要花费大量时间。问题二、中间层和权重矩阵的乘积以及Softmax层的计算。需要大量的计算,花费大量时间。解决问题一:计算one-hot表示矩阵和权重矩阵的乘积,其实就是将权重矩阵的某个特定的行取出来。如下图所示。Embedding层

    2025年9月26日
    8
  • 约瑟夫环问题–递归解法的理解

    约瑟夫环问题–递归解法的理解转自:点击打开链接这里还有一篇思路简单清晰的文章:http://blog.csdn.net/wusuopubupt/article/details/18214999先来看这个类型的某个题目描述:约瑟夫生者死者游戏约瑟夫游戏的大意:30个游客同乘一条船,因为严重超载,加上风浪大作,危险万分。因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免于难。无奈,大家只得同意这种办法,并议定3…

    2022年6月3日
    41
  • lasso回归-回归实操

    lasso回归-回归实操pythonlasso回归算法之回归实操基本概念正则化正则化是指对模型做显式约束,以避免过拟合。本文用到的lasso回归就是L1正则化。(从数学的观点来看,lasso惩罚了系数向量的L1范数,换句话说,就是系数的绝对值之和。)正则化的具体原理就不在这里多叙述了,感兴趣的朋友可以看一下这篇文章:机器学习中正则化项L1和L2的直观理解。算法简介lasso回归在了解lasso回归之前,建议朋友们先对普通最小二乘法和岭回归做一些了解,可以参考这两篇文章:最小二乘法-回归实操,岭回归-回归实操。除了

    2022年5月8日
    63

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号