【问题标题】:issues with deques and exceeding deque size with index operator双端队列问题和索引运算符超出双端队列大小
【发布时间】:2015-01-13 15:29:26
【问题描述】:

C++ 中的双端队列有一个奇怪的问题。 假设我有一个大小为 4 的双端队列。出于某种原因,当使用索引运算符时,我似乎能够超过双端队列的大小。 换句话说,如果我编写以下代码,编译器和执行中的程序都不会出错:

for(int i = 0; i < 7; i++)
{
    x[i] = (double)(i*i);
    cout << x[i] << endl;
}

x 是双端队列。我实际上能够从中获得输出。 它不会增加双端队列的大小。如果我输出 x.size(),我仍然得到 4。 是什么赋予了? 我正在使用 Code::Blocks 和它附带的标准默认 gcc 编译器。

【问题讨论】:

  • 越界就是调用未定义的行为,此时您的程序可以执行任何操作,包括擦除硬盘驱动器。
  • 普通数组也会发生同样的事情。你为什么感到惊讶?
  • neither the compiler 编译器如何知道只有 4 项? nor the program at execution will barf if I write the following我会问你——你所说的“执行会失败”是什么意思?如果你不能回答那个问题,那么你的不回答就是的答案。当你做这样的事情时,没有人知道程序会做什么。这就是你所说的“未定义行为”。
  • 通常,当我过去处理过这个问题时,编译器会抱怨,或者程序会在运行时给我一些异常。这也没有。对于大小为 4 的双端队列,从 i = 0 到 6,它返回:x[0] = 0, x[1] = 1, x[2] = 4, x[3] = 9, x[ 4] = 16,x[5] = 25,x[6] = 36。

标签: c++ stl deque


【解决方案1】:

operator[] 不进行边界检查,就像使用原始数组时一样。 at 成员函数可以,如果您改为使用

x.at(i);

如果超出deque 的界限,您将收到std::out_of_range 异常。如果您通过内存错误检查器(如 valgrind)运行原始代码,您将看到“无效读取”和“无效写入”错误。

如果您查看cppreference's docs on operator[],您会看到注释“未执行边界检查”。

不过the docs for at()

如果 pos 不在容器的范围内,则抛出 std::out_of_range 类型的异常

容器越界是未定义的行为。如果您使用不确定是否在边界内的索引进行访问,则您的工作是检查它是否是,或者使用 at 并可能处理异常。

【讨论】:

    【解决方案2】:

    索引越界会产生未定义的行为,因此任何事情都可能发生。

    许多容器会将当前大小四舍五入到某个方便的值(例如,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++ 标准),它(做/将)允许以下一般顺序:

    copy(x, output_range<double>(std::cout, "\n"));
    

    由于它自己计算出x 的范围,因此该范围的代码中没有错误,因此几乎不可能以这种方式意外索引超出范围。

    【讨论】:

      猜你喜欢
      • 2020-11-08
      • 2018-07-02
      • 1970-01-01
      • 2011-10-14
      • 2016-01-19
      • 1970-01-01
      • 2021-12-13
      • 2016-02-11
      • 2018-09-08
      相关资源
      最近更新 更多