在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; }

在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;

这段代码使用指针算术,据我所知,在数组中有一个指向最后一个元素的指针是有效的,引用该指针是未指定的,但我们可以有一个指向该位置的指针。 因此,&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;
    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;
}