简体   繁体   English

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

[英]std::vector and memory error when resizing

I have a structure defined like this: 我有一个像这样定义的结构:

struct Edge
{
    int u, v;   // vertices

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

and a class field defined like 和一个类定义的类字段

vector<Edge> solution;

In one of the methods I'm creating new Edge s and pushing them into the vector like this (a huge simplification of my real code, but the problem still exists): 在其中一种方法中,我正在创建新的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

When the last push_back is executing, I'm getting an error window in Visual Studio's debug mode 当最后一个push_back正在执行时,我在Visual Studio的调试模式下会出现一个错误窗口

[AppName] has triggered a breakpoint. [AppName]触发了一个断点。

and the debugger goes to malloc.c , to the end of _heap_alloc function. 并且调试器转到malloc.c ,到_heap_alloc函数的末尾。 Before this 7th line, the vector seems to work properly. 在第7行之前,向量似乎正常工作。 I can see all the elements in the debugger. 我可以看到调试器中的所有元素。 It seems that the vector has a problem reallocating itself (expanding its size). 似乎向量在重新分配自身(扩大其大小)方面存在问题。

What's interesting, if I put this before all the pushing back: 有趣的是,如果我把它放在所有推回之前:

solution.reserve(7);

, the 7th edge is added properly. ,正确添加第7个边缘。 What's even more interesting, trying to reserve space for more than 22 elements also causes the mentioned error. 更有趣的是,尝试为超过22个元素预留空间也会导致上述错误。

What am I doing wrong? 我究竟做错了什么? How can I debug it? 我该怎么调试呢? The rest of the application doesn't use so much memory, so I can't believe the heap is full. 应用程序的其余部分没有使用这么多内存,所以我无法相信堆已满。


More code, on request. 根据要求提供更多代码。 It's a rather sloppy implementation of 2-approximation algorithm for Metric Travelling Salesman's Problem. 对于度量旅行商问题,这是一种相当草率的2近似算法。 It first creates a minimum spanning tree, then adds vertices (just indices) to the partialSolution vector in the DFS order. 它首先创建最小生成树,然后将顶点(仅索引)添加到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 : 来自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();

A part of TSPSolver.c: 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();
}

This is rather a comment then an answer, but the space is too limited. 这是一个评论,然后是答案,但空间太有限了。

If you are on windows, try Microsoft Application Verifier . 如果您在Windows上,请尝试Microsoft Application Verifier It might detect wrong memory access. 它可能检测到错误的内存访问。

Another way of detecting such access is to reseve empty char arrays initialized to 0. 检测此类访问的另一种方法是重新初始化为0的空char数组。

Open your class where the vector is declared and declare a char array of let's say 64 chars before and after your vector and initialize them to 0!. 打开声明向量的类,并在向量之前和之后声明一个char数组,让我们说64个字符,并将它们初始化为0!。 Then break into your vector code, where the error is generated and check the contents of those padding arrays. 然后进入矢量代码,生成错误并检查这些填充数组的内容。 If they are filled, somebody writes more that it should. 如果它们被填满,那么有人会写更多它应该。

A way to locate the "malicious" access (at least in VC++) is to set a data breakpoint writing in your padding arrays and check the callstack then. 找到“恶意”访问(至少在VC ++中)的一种方法是在填充数组中设置数据断点,然后检查callstack。

You may be doing out-of-bounds accesses on points in various places, eg this one: 您可能正在对各个地方的points进行越界访问,例如:

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

What if input failed, or the value is out of range? 如果输入失败或值超出范围怎么办?

I would suggest removing all uses of new and delete and [] from your code; 我建议从代码中delete newdelete[]所有用法; eg assuming points is int *points; 例如假设pointsint *points; then replace it with std::vector<int> points . 然后用std::vector<int> points替换它。 Change all [] accesses to be .at() and catch exceptions. 将所有[]访问更改为.at()并捕获异常。 Disable copying on all classes that don't have correct copy semantics. 禁用所有没有正确复制语义的类的复制。

Then you can be more certain that it is not a memory allocation error, copying error, or out-of-bounds access (which are strong candidates for explaining your symptoms). 然后,您可以更加确定它不是内存分配错误,复制错误或越界访问(这是解释您的症状的强有力候选者)。

This would also fix the problem that TSPSolver currently does not have correct copy semantics. 这也可以解决TSPSolver目前没有正确复制语义的问题。

It would be very useful to make a SSCCE. 制作SSCCE非常有用。 You mention that there is "a lot of input", try reducing the input as small as you can but still have the problem occur. 你提到“有很多输入”,尽量减少输入,但仍然会出现问题。 An SSCCE can include input data so long as it is a manageable size that you can post. SSCCE可以包含输入数据,只要它是可以发布的可管理大小即可。 Currently you show too much code but not enough, as they say. 正如他们所说,目前你显示的代码太多但不够。 The problem is still lurking somewhere you haven't posted yet. 这个问题仍然潜伏在你尚未发布的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM