在C ++中有一个奇怪的deques问题。 让我们说我有一个大小为4的双精灵双胞胎。出于某种原因,当使用索引运算符时,我似乎能够超过双端队列的大小。 换句话说,如果我写下以下内容,编译器和执行程序都不会barf:
for(int i = 0; i < 7; i++)
{
x[i] = (double)(i*i);
cout << x[i] << endl;
}
其中x是双端队列。而我实际上能够从中获得输出。 它不会增加双端队列的大小。如果我输出x.size(),我仍然得到4。 是什么赋予了? 我使用Code :: Blocks和它附带的标准默认gcc编译器。
答案 0 :(得分:3)
operator[]
没有检查边界,就像使用原始数组一样。 at
成员函数,如果您改为使用
x.at(i);
如果超出std::out_of_range
的范围,您将获得deque
例外。如果您通过内存错误检查程序(如valgrind)运行原始代码,您将看到&#34;无效读取&#34;和&#34;无效写&#34;错误。
如果您查看cppreference's docs on operator[],您会看到注释&#34;不会执行边界检查。&#34;
如果pos不在容器范围内,则抛出类型为std :: out_of_range的异常
离开容器的界限是未定义的行为。如果您使用索引进行访问而不确定其是否在入境,那么您的工作就是检查它是否正确,或者使用at
并且可能使用{{1}}处理异常。
答案 1 :(得分:1)
索引超出范围会产生未定义的行为,因此任何事情都可能发生。
许多容器会将当前大小舍入到一些方便的值(例如,2的幂),因此根据当前大小,您将在集合中的最后一项之后拥有一定量的内存。索引到该内存并尝试读取它会产生一些结果,但内存通常是未初始化的,因此结果通常是无意义和无效的(并且,尽管大多数情况下,容器可以进行边界检查,并抛出当你索引越界时,一个异常或几乎任何其他东西。)
IMO,at
是处理这种可能性的相当差的工具。避免此类问题的更好方法是基于范围的for
循环:
for (auto &d : x) {
d = d * d;
std::cout << d << "\n"; // avoid `endl`, which flushes the stream.
}
另一种可能性是使用标准算法:
std::transform(x.begin(), x.end(), x.begin(), [](double d) { return d*d; });
std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, "\n"));
还有基于范围的算法(例如,Boost中的一个集合,至少还有一个建议用于未来的C ++标准),(do / would)允许按以下顺序执行某些操作:
copy(x, output_range<double>(std::cout, "\n"));
由于这会自己找出x
的范围,缺少范围代码中的错误,因此很难以这种方式意外地索引越界。