调整大小时std :: vector和内存错误

时间:2014-06-10 11:35:15

标签: c++ stdvector

我的结构定义如下:

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行之前,向量似乎正常工作。我可以看到调试器中的所有元素。似乎向量在重新分配自身(扩大其大小)方面存在问题。

有趣的是,如果我把它放在所有推回之前:

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

来自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();

TSPSolver.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();
}

2 个答案:

答案 0 :(得分:0)

这是一个评论然后是答案,但空间太有限了。

如果您在Windows上,请尝试Microsoft Application Verifier。它可能会检测到错误的内存访问。

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

打开声明向量的类,并在向量之前和之后声明一个char数组,让我们说64个字符,并将它们初始化为0!。 然后进入矢量代码,生成错误并检查这些填充数组的内容。如果它们被填满,那么有人会写更多内容。

找到“恶意”访问权限的方法(至少在VC ++中)是在填充数组中设置数据断点,然后检查callstack。

答案 1 :(得分:0)

您可能会在points的各个地方进行越界访问,例如这一个:

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

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

我建议从代码中删除newdelete以及[]的所有用途;例如假设pointsint *points;,则将其替换为std::vector<int> points。将所有[]次访问权限更改为.at()并捕获例外情况。禁止复制所有没有正确复制语义的类。

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

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

制作SSCCE非常有用。你提到“有很多输入”,尽量减少输入,但仍然会出现问题。 SSCCE可以包含输入数据,只要它是可以发布的可管理大小即可。正如他们所说,目前你显示的代码太多但不够。问题仍然存在于尚未发布的地方。