近期工作中遇到在访问vector
越界问题,本文小结此问题。
问题描述
通过某种操作,触发vector
访问越界,在debug
环境上崩溃,而release
环境上没有崩溃。
问题分析
在往vector
中添加元素时,会分配大于实际元素个数的空间。具体分配策略关键源码如下:
size_type _Grow_to(size_type _Count) const
{ // grow by 50% or at least to _Count
size_type _Capacity = capacity();
_Capacity = max_size() - _Capacity / 2 < _Capacity
? 0 : _Capacity + _Capacity / 2; // try to grow by 50%
if (_Capacity < _Count)
_Capacity = _Count;
return (_Capacity);
}
max_size()
为vector
中预定于的最大容量,当容量(_Capacity
)不够时,以 (_Capacity
+ _Capacity
/2) 作为新容量进行扩容,也就是按照当前容量的50%来增长。
[]取值函数如下:
const_reference operator[](size_type _Pos) const
{ // subscript nonmutable sequence
#if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
{ // report error
_DEBUG_ERROR("vector subscript out of range");
_SCL_SECURE_OUT_OF_RANGE;
}
#elif _ITERATOR_DEBUG_LEVEL == 1
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
#endif /* _ITERATOR_DEBUG_LEVEL */
return (*(this->_Myfirst() + _Pos));
}
由此可见,在Debug版本下,使用[]
方式取值,会触发异常,Release版本不会。
at取值函数如下:
const_reference at(size_type _Pos) const
{ // subscript nonmutable sequence with checking
if (size() <= _Pos)
_Xout_of_range("invalid vector<T> subscript");
return (*(this->_Myfirst() + _Pos));
}
使用at
函数,不管Debug
还是Release
,都会判断下标有效性,越界则报异常。
小结
综上所述,对于vector中已分配但尚未使用的空间,使用[]
方式,在Debug上会触发异常,Release
不会。虽说Release
不触发异常,但后续读写到的是未定义数值,行为不确定,要坚决避免。
以at
方式读写,下标只能是真实数据下标范围,不能越界。
另外一个知识点:vector的reserve函数只分配空间,resize函数既分配空间也进行初始化。