调整大小时出现Std::vector和内存错误

std::vector and memory error when resizing

本文关键字:vector 内存 错误 Std 小时 调整      更新时间:2023-10-16

我有一个这样定义的结构:

struct Edge
{
    int u, v;   // vertices
    Edge() { }
    Edge(int u, int v)
    {
        this->u = u;
        this->v = v;
    }
};

和定义为

的类字段
vector<Edge> solution;

在其中一种方法中,我创建了新的Edge,并将它们推入这样的向量中(对我的实际代码进行了巨大的简化,但问题仍然存在):

solution.push_back(Edge(1, 2));
solution.push_back(Edge(3, 4));
solution.push_back(Edge(5, 6));
solution.push_back(Edge(7, 8));
solution.push_back(Edge(9, 10));
solution.push_back(Edge(11, 12));
solution.push_back(Edge(13, 14)); // adding 7th element; the problem occurs here

当最后一个push_back正在执行时,我在Visual Studio的调试模式中得到一个错误窗口

[AppName]触发断点

,调试器转到malloc.c,直到_heap_alloc功能结束。在第7行之前,向量似乎工作正常。我可以看到调试器中的所有元素。似乎vector在重新分配自身(扩展其大小)方面有问题。

有趣的是,如果我把这个放在所有的反推之前:

solution.reserve(7);

,第7条边添加正确。更有趣的是,试图为超过22个元素保留空间也会导致上述错误。

我做错了什么?我如何调试它?应用程序的其余部分不使用那么多内存,所以我不敢相信堆已经满了。


更多的代码,根据需要。这是度量旅行推销员问题的2-近似算法的一个相当草率的实现。它首先创建一个最小生成树,然后按DFS顺序向partialSolution向量添加顶点(只是索引)。

void ApproxTSPSolver::Solve()
{
    // creating a incidence matrix
    SquareMatrix<float> graph(noOfPoints);
    for (int r=0; r<noOfPoints; r++)
    {
        for (int c=0; c<noOfPoints; c++)
        {
            if (r == c)
                graph.SetValue(r, c, MAX);
            else
                graph.SetValue(r, c, points[r].distance(points[c]));
        }
    }
    // finding a minimum spanning tree
    spanningTree = SquareMatrix<bool>(noOfPoints);
    // zeroeing the matrix
    for (int r=0; r<noOfPoints; r++)
        for (int c=0; c<noOfPoints; c++)
            spanningTree.SetValue(r, c, false);
    bool* selected = new bool[noOfPoints];
    memset(selected, 0, noOfPoints*sizeof(bool));
    selected[0] = true; // the first point is initially selected
    float min;
    int minR, minC;
    for (int i=0; i<noOfPoints - 1; i++)
    {
        min = MAX;
        for (int r=0; r<noOfPoints; r++)
        {
            if (selected[r] == false)
                continue;
            for (int c=0; c<noOfPoints; c++)
            {
                if (selected[c] == false && graph.GetValue(r, c) < min)
                {
                    min = graph.GetValue(r, c);
                    minR = r;
                    minC = c;
                }
            }
        }
        selected[minC] = true;
        spanningTree.SetValue(minR, minC, true);
    }
    delete[] selected;
    // traversing the tree
    DFS(0);
    minSol = 0.0f;
    // rewriting the solution to the solver's solution field
    for (int i=0; i<noOfPoints - 1; i++)
    {
        solution.push_back(Edge(partialSolution[i], partialSolution[i + 1]));
        minSol += points[partialSolution[i]].distance(points[partialSolution[i + 1]]);
    }
    solution.push_back(Edge(partialSolution[noOfPoints - 1], partialSolution[0]));
    minSol += points[partialSolution[noOfPoints - 1]].distance(points[partialSolution[0]]);
    cout << endl << minSol << endl;
    solved = true;
}
void ApproxTSPSolver::DFS(int vertex)
{
    bool isPresent = std::find(partialSolution.begin(), partialSolution.end(), vertex)
        != partialSolution.end();
    if (isPresent == false)
        partialSolution.push_back(vertex); // if I comment out this line, the error doesn't occur
    for (int i=0; i<spanningTree.GetSize(); i++)
    {
        if (spanningTree.GetValue(vertex, i) == true)
            DFS(i);
    }
}

class ApproxTSPSolver : public TSPSolver
{
    vector<int> partialSolution;
    SquareMatrix<bool> spanningTree;
    void DFS(int vertex);
public:
    void Solve() override;
};

from main.cpp:

TSPSolver* solver;
    string inputFilePath, outputFilePath;
    // parsing arguments
    if (ArgParser::CmdOptionExists(argv, argv + argc, "/a"))
    {
        solver = new ApproxTSPSolver();
    }
    else if (ArgParser::CmdOptionExists(argv, argv + argc, "/b"))
    {
        solver = new BruteForceTSPSolver();
    }
    else
    {
        solver = new BranchAndBoundTSPSolver();
    }
    inputFilePath = ArgParser::GetCmdOption(argv, argv + argc, "/i");
    outputFilePath = ArgParser::GetCmdOption(argv, argv + argc, "/s");
    solver->LoadFromFile(inputFilePath);
    Timer timer;
    timer.start();
    solver->Solve();
    timer.stop();
    cout << timer.getElapsedTime();

tspsolve .c的一部分:

TSPSolver::TSPSolver()
{
    points = NULL;
    solved = false;
}
TSPSolver::~TSPSolver()
{
    if (points)
        delete[] points;
}
void TSPSolver::LoadFromFile(string path)
{
    ifstream input(path);
    string line;
    int nodeID;
    float coordX, coordY;
    bool coords = false;
    minX = numeric_limits<float>::max();
    maxX = numeric_limits<float>::min();
    minY = numeric_limits<float>::max();
    maxY = numeric_limits<float>::min();
    while (input.good())
    {
        if (coords == false)
        {
            getline(input, line);
            if (line == "NODE_COORD_SECTION")
            {
                coords = true;
            }
            else if (line.find("DIMENSION") != string::npos)
            {
                int colonPos = line.find_last_of(":");
                noOfPoints = stoi(line.substr(colonPos + 1));
#ifdef _DEBUG
                cout << noOfPoints << " points" << endl;
#endif
                // allocating memory for this amount of points
                points = new Point[noOfPoints];
            }
        }
        else
        {
            input >> nodeID >> coordX >> coordY;
            points[nodeID - 1].X = coordX;
            points[nodeID - 1].Y = coordY;
            minX = min(minX, coordX);
            maxX = max(maxX, coordX);
            minY = min(minY, coordY);
            maxY = max(maxY, coordY);
            if (nodeID == noOfPoints)
            {
                break;
            }
        }
    }
    input.close();
}

与其说是回答,不如说是评论,但是篇幅太有限。

如果您在windows上,请尝试Microsoft应用程序验证程序。它可能检测到错误的内存访问。

另一种检测此类访问的方法是保留初始化为0的空char数组。

打开声明vector的类,在vector的前后分别声明64个字符的char数组,并将它们初始化为0!然后进入生成错误的矢量代码,检查这些填充数组的内容。如果有人填满了,那一定是有人写得太多了。

查找"恶意"访问的一种方法(至少在vc++中)是在填充数组中设置一个数据断点写入,然后检查调用堆栈

您可能会在不同的地方对points进行越界访问,例如:

input >> nodeID >> coordX >> coordY;
points[nodeID - 1].X = coordX;

如果输入失败或值超出范围怎么办?

我建议从你的代码中删除newdelete[]的所有使用;例如,假设pointsint *points;,那么将其替换为std::vector<int> points。将所有[]访问更改为.at()并捕获异常。在所有没有正确复制语义的类上禁用复制。

然后您可以更确定这不是内存分配错误、复制错误或越界访问(这些都是解释您的症状的有力候选)。

这也将解决TSPSolver目前没有正确的复制语义的问题。

制作一个SSCCE是非常有用的。你提到有"大量的输入",试着尽可能减少输入,但问题仍然会发生。SSCCE可以包括输入数据,只要它是一个可管理的大小,您可以发布。正如他们所说,目前您显示的代码太多,但还不够。