【问题标题】:std::vector<std::vector<int>>: Debug assertion failed. C++ vector subscript out of range reserving memory [duplicate]std::vector<std::vector<int>>:调试断言失败。 C ++向量下标超出范围保留内存[重复]
【发布时间】:2019-10-27 03:48:06
【问题描述】:

这里有什么问题?

std::vector<std::vector<int>> mSectionsSubsets;

int count = (int)powf(2, NUM_SECTIONS);

mSectionsSubsets.reserve(count);
for (int i = 0; i < count; i++) {
    mSectionsSubsets[i].reserve(NUM_SECTIONS);
}

在 MSVC++ 上,当我在第一个 i = 0 时显示 mSectionsSubsets[i].reserve(NUM_SECTIONS); 时,它会显示 vector subscript out of range

现在确定出了什么问题以及如何解决它。

【问题讨论】:

  • reserve 不会改变向量的大小。

标签: c++ memory vector std


【解决方案1】:

你写了mSectionsSubsets[i]i0count

这些访问中的每一个都是非法的,因为mSectionsSubsets 中没有任何元素。

保留容量和调整向量大小是两件不同的事情。

在这种特殊情况下,也许:

mSectionsSubsets.resize(count);
for (int i = 0; i < count; i++) {
    mSectionsSubsets[i].reserve(NUM_SECTIONS);
}

但是,总体而言,我会警告不要使用向量的向量:它们对您的缓存有害,而且当您的尺寸为方形时通常不需要。

换个漂亮的std::vector&lt;int&gt; 怎么样?如果您需要 count*NUM_SECTIONS 元素,那么只需执行此操作。您始终可以为其索引创建二维外观:

i = x + width*y

缓存来自 cmets 的快速模型:

#include <iostream>
#include <vector>
#include <stdexcept>
#include <cassert>

// Like a vector<vector<T>>, but *better*!
template <typename T>
class RowList
{
public:
    RowList(const std::size_t rowCount, const std::size_t maxRowLength)
        : rowCount(rowCount)
        , maxRowLength(maxRowLength)
        , data(rowCount * maxRowLength)
        , utilisation(rowCount)
    {}

    std::size_t getRowCount() const
    {
        return rowCount;
    }

    std::size_t getMaxRowLength() const
    {
        return maxRowLength;
    }

    // UB if you give an invalid row number
    std::size_t getRowLength(const std::size_t rowNumber) const
    {
        assert(rowNumber < rowCount);
        return utilisation[rowNumber];
    }

    // UB if you give an invalid row number
    void clearRow(const std::size_t rowNumber)
    {
        assert(rowNumber < rowCount);
        utilisation[rowNumber] = 0;

        #ifdef NDEBUG
            // Debug builds only - make all the dead values -1
            // so we can maybe more easily spot misuse
            const std::size_t start = rowNumber*maxRowLength;
            const std::size_t end   = start + maxRowLength;

            for (std::size_t i = start; i < end; ++i)
                data[i] = -1;
        #endif
    }

    // UB if you give an invalid row number
    // throws std::out_of_range if the row is full
    void pushToRow(const std::size_t rowNumber, T value)
    {
        assert(rowNumber < rowCount);

        std::size_t& columnNumber = utilisation[rowNumber];
        if (columnNumber == maxRowLength)
            throw std::out_of_range("Row is full!");

        data[rowNumber*maxRowLength + columnNumber] = std::move(value);
        columnNumber++;
    }

    // UB if you give an invalid row or column number
    T& elementAt(const std::size_t rowNumber, const std::size_t columnNumber)
    {
        assert(rowNumber < rowCount);
        assert(columnNumber < utilisation[rowNumber]);

        return data[rowNumber*maxRowLength + columnNumber];
    }

    // UB if you give an invalid row or column number
    const T& elementAt(const std::size_t rowNumber, const std::size_t columnNumber) const
    {
        assert(rowNumber < rowCount);
        assert(columnNumber < utilisation[rowNumber]);

        return data[rowNumber*maxRowLength + columnNumber];
    }

private:
    const std::size_t rowCount;
    const std::size_t maxRowLength;

    std::vector<T> data;
    std::vector<std::size_t> utilisation;
};

template <typename T>
std::ostream& operator<<(std::ostream& os, const RowList<T>& matrix)
{
    const auto height = matrix.getRowCount();
    for (std::size_t y = 0; y < height; ++y)
    {
        const auto width = matrix.getRowLength(y);
        const auto remainder = matrix.getMaxRowLength() - width;

        for (std::size_t x = 0; x < width; ++x)
            os << matrix.elementAt(y, x) << '\t';

        for (std::size_t i = 0; i < remainder; ++i)
            os << "?\t";

        os << '\n';
    }

    return os;
}

int main()
{
    RowList<int> matrix(5, 5);

    matrix.pushToRow(2, 100);
    matrix.pushToRow(2, 101);
    matrix.pushToRow(4, 102);
    std::cerr << matrix << '\n';

    matrix.clearRow(2);
    matrix.pushToRow(1, 103);
    std::cerr << matrix << '\n';
}

 

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
?   ?   ?   ?   ?   
?   ?   ?   ?   ?   
100 101 ?   ?   ?   
?   ?   ?   ?   ?   
102 ?   ?   ?   ?   

?   ?   ?   ?   ?   
103 ?   ?   ?   ?   
?   ?   ?   ?   ?   
?   ?   ?   ?   ?   
102 ?   ?   ?   ?   

【讨论】:

  • 这个答案的后半部分确实包含了非常好的建议。上升了。
  • @Lightness Races in Orbit 它不是“方形”。我正在为我可以达到的内容创造“优势”,但有些元素可能会更低。我需要的不是在运行时分配内存。所以我提前游戏,并在此期间 clear() 和 push (在此过程中允许正确的大小)。
  • 卓越的 C++ 第 1 章。向量的使用和滥用准确地描述了这一点
  • @markzzz 就内存消耗而言,您的容器是方形的,因此您还不如真正拥有一个方形容器。您不必填写每个“行”。这也将帮助您减少使用,因为您不需要count*vector 机器。虽然我承认听起来你需要一些方法来跟踪行利用率。
  • 如我所说。使用单个向量。地图索引。如果需要,可以跟踪利用率(我认为,每行包含“使用的单元格”的单个其他向量可以做到这一点并且从长远来看仍然更便宜 - 但要确定它!)。你会把它包装在一个类中。你可以给它clearRow(i)pushToRow(i, val),只要你喜欢:) (嗯,直到你构造它的“最大行长度”!)
【解决方案2】:

reserve 不设置大小,它设置容量(即它更关心内存管理)。在您的情况下,mSectionsSubsetsmSectionsSubsets.reserve(count); 之后的大小仍然为零。

使用resize 设置向量的大小。

请注意,使用.at 而不是[] 更安全,因为在前一种情况下,将抛出运行时异常,而不是简单地未定义程序行为。

【讨论】:

  • 然而,至少在调试中,至少在 VS 上,OP 的结果表明,使用.at()(特别是在这样的小循环中)降低程序速度并不总是需要的!
  • @LightnessRacesInOrbit 这可能会让你大吃一惊,但在我的店里我已经禁止[],除非你能证明不使用它会严重降低性能。它在极少数地方生存。
  • ?????? 为什么不直接切换到 Java 并完成它:P
  • @LightnessRacesinOrbit 不确定我是否误解了,但在我的 VS 版本中,.at 与发布相比,在调试版本中的额外开销为零(它总是检查范围)。 [] 仅使用 _ITERATOR_DEBUG_LEVEL != 0 进行范围检查。所以我不太明白你所说的“至少在调试中”是什么意思......
  • @LightnessRacesinOrbit:Java 是一种用于编写定量代码的极其有害的语言(没有运算符重载、== 比较引用、整数缓存)。我知道你在开玩笑!
猜你喜欢
  • 2016-02-14
  • 2016-05-02
  • 1970-01-01
  • 2018-10-23
  • 2011-08-30
  • 2017-06-18
  • 2021-12-22
  • 1970-01-01
  • 2021-06-29
相关资源
最近更新 更多