使用运算符 [] 引用 std::vector 上最后一个元素时出现问题<>
Problems referencing one past last element on std::vector<> using operator[]
给定此有效的 C 或 C++ 代码
int x() {
int numbers[3]; // Lets suppose numbers are filled in with values
int sum = 0;
for (const int* p = &numbers[0]; p != &numbers[3]; ++p)
sum += *p;
return sum;
}
这段代码使用指针算法,据我所知,有一个指针指向数组中最后一个元素是有效的,引用该指针是未指定的,但我们可以让指针指向该位置。 因此,&p[0]、&p[1]、&p[2] 和 &p[3] 是有效的指针,p[0]、p[1] 和 p[2] 是有效值。
如果我用std::vector<int>
替换 int 数组,一切都应该没问题,我们得到这段代码
#include <vector>
int x() {
std::vector<int> numbers(3);
int sum = 0;
for (const int* p = &numbers[0]; p != &numbers[3]; ++p)
sum += *p;
return sum;
}
但是在 DEBUG 模式下的 Visual C++ 2017 下运行,我得到这个异常"矢量下标超出范围",这是从 MS STL 库触发的,因为实现假设使用 operator[] 我们会自动引用底层值,但事实并非如此。 这是执行边界检查的 MS STL 代码...
_NODISCARD _Ty& operator[](const size_type _Pos)
{ // subscript mutable sequence
#if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
{ // report error
_DEBUG_ERROR("vector subscript out of range");
}
#elif _ITERATOR_DEBUG_LEVEL == 1
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
#endif /* _ITERATOR_DEBUG_LEVEL */
return (this->_Myfirst()[_Pos]);
}
如果我将 &numbers[0] 和 &numbers[3] 替换为 numbers.begin(( 和 numbers.end((,则不会收到错误。
我同意这是非常丑陋的代码,但我简化真正的代码只是为了暴露错误。
原始代码在元素为零的向量上使用 &vec[0]。
所以我的问题是:
这是Microsoft视觉C++ STL 实现上的错误,还是对向量<>的运算符 [] 有一些限制?
我知道用at((替换[]将是一个错误,但我知道&vec[size]应该仍然对std::vector有效<>
是否明确定义了获取指向最后一个&p[n]
元素的指针,这一直是一个灰色区域。
但是,指向最后一个元素之后的指针是明确定义的。
您可以通过使用普通指针算法来避免此类错误:
for (const int* p = numbers.data(); p != numbers.data() + 3; ++p)
或者,更一般地说,迭代器:
using std::begin;
using std::end;
for(auto p = begin(v), q = end(v); p != q; ++p)
或者使用循环范围:
for(auto const& element : v)
真的没有充分的理由使用v[v.size()]
。
VisualC++ 中的调试迭代器库正确报告了问题,并且问题并不微妙。
根据标准,[sequence.reqmts] 表 101,a
提供operator[]
类型的序列容器的表达式a[n]
(std::vector
,就像basic_string
、deque
和array
一样(,具有*(a.begin()+n)
的操作语义。但是,在您正在运行的情况下,a.begin()+n
将等效于a.end()
.因此,在应用地址运算符之前*(a.end())
结果。
取消引用容器的end()
迭代器会调用未定义的行为。视觉C++在报告断言时是正确的,您最好更改枚举策略。
取消引用过去最后一个元素会导致未定义的行为。如果声明std::vector<int> numbers(3)
则允许访问的最后一个元素是numbers[2]
。原始数组的故事相同。
如果可以,请避免使用原始数组:
int x() {
std::vector<int> numbers(3);
//...
int sum = 0;
for (auto value : numbers)
sum += value;
return sum;
}
因此,&p[0]、&p[1]、&p[2] 和 &p[3] 是有效的指针
不。数组的下标运算符(p[x]
(是*(p+x)
的语法糖,所以&p[3]
实际上是在做&(*(p+3))
但*(p+3)
是未定义的行为!!
如果您想要的只是一个过去的地址,那么p+3
是完全有效的。这将使用指针算法,并且不会取消引用任何内容。
如果我用 std::vector 替换 int 数组,一切都应该没问题
再说一遍,不!如果尝试取消引用尚未分配的内存位置,将出现未定义的行为。std::vector
没有说明v[v.length()]
被分配给你,所以这是未定义的行为。
该实现假设使用 operator[] 我们自动引用底层值,但事实并非如此
是的,它是!!阅读operator[]
上的 cppreference 页面:">返回对指定位置位置的元素的引用。 不执行边界检查。就像原始数组一样,这里的下标运算符返回对基础值的引用,这意味着这里涉及取消引用步骤!
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 使用strcpy将char数组的元素复制到另一个数组
- 使用不带参数的函数访问结构元素
- 给定n个元素的m个集合.在C++中找到出现在最大集合数中的元素
- C++如何通过用户输入删除列表元素
- lower_bound()返回最后一个元素
- 基于多个条件处理地图中的所有元素
- 调整大小后指向元素值的指针unordered_map有效?
- 使用std::transform将一个范围的元素添加到另一个范围中
- 使用函数"remove"删除重复元素
- 具有最大子序列大小的序列,每个元素都相同
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 如何将元素添加到数组的线程安全函数?
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- 我想访问std::unique_ptr中的一个特定元素
- 如何通过 getter 函数删除矢量的元素?
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 从控制台中删除最后打印的元素
- 擦除while循环中迭代的元素