在std::vector<;上引用最后一个元素时出现问题&燃气轮机;使用运算符[] 给出了这个有效的C或C++代码< /P> 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数组,一切都会好起来,我们得到以下代码在std::vector<;上引用最后一个元素时出现问题&燃气轮机;使用运算符[] 给出了这个有效的C或C++代码< /P> 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; },c++,C++,这段代码使用指针算术,据我所知,在数组中有一个指向最后一个元素的指针是有效的,引用该指针是未指定的,但我们可以有一个指向该位置的指针。 因此,&p[0]、&p[1]、&p[2]和&p[3]是有效指针,而p[0]、p[1]和p[2]是有效值 如果我用std::vector替换int数组,一切都会好起来,我们得到以下代码 #include <vector> int x() { std::vector<int> numbers(3); int sum = 0;
#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;
}
#包括
int x(){
std::向量数(3);
整数和=0;
for(常数int*p=&numbers[0];p!=&numbers[3];++p)
总和+=*p;
回报金额;
}
但在VisualC++ 2017中运行调试模式时,我从MS STL库中触发了这个异常“向量下标”,因为该实现表明使用操作符[]我们自动引用底层值,而不是这样。 这是做边界检查的MSSTL代码
_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]);
}
\u NODISCARD类型和运算符[](常量大小类型位置)
{//下标可变序列
#如果_迭代器_调试_级别==2
如果(size()_Myfirst()[_Pos]);
}
如果用numbers.begin()和numbers.end()替换&numbers[0]和&numbers[3],则不会出现错误
我同意这是非常难看的代码,但我简化了真正的代码只是为了暴露错误。
原始代码在一个包含零元素的向量上使用&vec[0]
所以我的问题是:
<强>这是微软VisualC++的STL实现中的一个bug,还是对向量[1/强]>/p>的运算符[]有一定的限制?
我知道用at()替换[]是一个错误,但我理解&vec[size]对std::vector仍然有效。获取指向最后一个元素的指针是否定义良好一直是一个灰色区域 但是,指向最后一个元素的指针定义良好 通过使用普通指针算法,可以避免此类错误:
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)
或使用循环的范围:
实际上,没有充分的理由使用
v[v.size()]
因此,&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()]
,因此这是未定义的行为
该实现要求使用运算符[]自动引用参考底图值,但实际情况并非如此
是的!!读:“返回对指定位置元素的引用。不执行边界检查。”与原始数组一样,这里的下标运算符返回对基础值的引用,这意味着这里涉及到一个取消引用步骤 > P > VisualC++中的调试迭代器库正正确地报告问题,问题并不微妙。 根据标准,[sequence.reqmts]表101,提供
运算符[]
类型的序列容器a
的表达式a[n]
(std::vector
与basic_string
、deque
和array
)具有*(a.begin()+n)的操作语义
。但是,在您正在运行的情况下,a.begin()+n
将等同于a.end()
。因此,在应用运算符地址之前,结果是*(a.end())
取消引用容器的
end()
迭代器将调用未定义的行为。VisualC++在报告断言方面是正确的,并且您将很好地改变枚举策略。 取消引用过去的最后一个元素会导致未定义的行为。如果声明std::vector numbers(3)
,则允许访问的最后一个元素是numbers[2]
。原始数组也是如此
如果可以,请避免使用原始阵列:
int x() {
std::vector<int> numbers(3);
//...
int sum = 0;
for (auto value : numbers)
sum += value;
return sum;
}
intx(){
std::向量数(3);
//...
整数和=0;
用于(自动值:数字)
总和+=数值;
回报金额;
}
我不相信&p[3]
是有效的指针,因为p[3]
一开始是无效的p+3
是一个有效的指针,它指向预期的位置,正如vec.begin()+3
这是MSV的“帮助”。如果不需要调试支持,请在发布模式下编译。另外,FWIW,实现这一点的标准方法是auto sum=std::accumulate(std::begin(container_name)、std::end(container_name)、container_name::value_type{})代码>@NathanOliver我很想使用0
而不是container\u name::value\u type{}
@Caleth-Yeah。当我写它的时候,我在想,如果有一个std::value\u类型
type特性,它将为容器或数组提供使用该类型的功能,那就太好了。通用编程FTW。你也可以使用迭代器,在这种情况下,迭代器与原始点相同。我知道,但我想知道这是否是向量实现上的错误,只是好奇而已。客户端和库代码都是Microsoft的财产,我只想知道在这方面哪一个是错误的@RicardoCapurro在这个非常流行的类中,它不太可能是Microsoft标准库中的一个bug。我非常确定,在末尾取消引用一个是未定义的行为。p+9999
不是
int x() {
std::vector<int> numbers(3);
//...
int sum = 0;
for (auto value : numbers)
sum += value;
return sum;
}