簡體   English   中英

調整大小時std :: vector和內存錯誤

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

我有一個像這樣定義的結構:

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

這是一個評論,然后是答案,但空間太有限了。

如果您在Windows上,請嘗試Microsoft Application Verifier 它可能檢測到錯誤的內存訪問。

檢測此類訪問的另一種方法是重新初始化為0的空char數組。

打開聲明向量的類,並在向量之前和之后聲明一個char數組,讓我們說64個字符,並將它們初始化為0!。 然后進入矢量代碼,生成錯誤並檢查這些填充數組的內容。 如果它們被填滿,那么有人會寫更多它應該。

找到“惡意”訪問(至少在VC ++中)的一種方法是在填充數組中設置數據斷點,然后檢查callstack。

您可能正在對各個地方的points進行越界訪問,例如:

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

如果輸入失敗或值超出范圍怎么辦?

我建議從代碼中delete newdelete[]所有用法; 例如假設pointsint *points; 然后用std::vector<int> points替換它。 將所有[]訪問更改為.at()並捕獲異常。 禁用所有沒有正確復制語義的類的復制。

然后,您可以更加確定它不是內存分配錯誤,復制錯誤或越界訪問(這是解釋您的症狀的強有力候選者)。

這也可以解決TSPSolver目前沒有正確復制語義的問題。

制作SSCCE非常有用。 你提到“有很多輸入”,盡量減少輸入,但仍然會出現問題。 SSCCE可以包含輸入數據,只要它是可以發布的可管理大小即可。 正如他們所說,目前你顯示的代碼太多但不夠。 這個問題仍然潛伏在你尚未發布的地方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM