C++列表的内存损坏问题

Posted

技术标签:

【中文标题】C++列表的内存损坏问题【英文标题】:The memory corruption problem of C++ list 【发布时间】:2021-12-16 10:51:14 【问题描述】:

我是 C++ 新手,我需要将列表容器用于我的基于 3D 标记的分水岭函数。但是当我使用列表容器时会遇到奇怪的错误。我可以知道我的代码有什么问题吗?

非常感谢!

我使用列表向量来保存等待搜索像素索引。 我是这样声明变量的(GVInt32就是int32_t):

vector<list<GVInt32>> toSearchList; 

而我使用了list的这两种操作:

    在列表末尾添加新的等待搜索索引
toSearchList[cnt].push_back(newidx);
    删除列表中间的搜索元素(itlist&lt;GVInt32&gt;::iterator):
it = toSearchList[cnt].erase(it);

但我得到两种错误:

    malloc(): memory corruption 当我这样做时 toSearchList[cnt].push_back(newidx);

    当我在调试器中检查变量时,列表末尾出现不可访问的元素:

[Not accessible elements][1]

https://i.stack.imgur.com/flJg3.png

IDE是QT creator 4.15.2

系统是Ubuntu 18.04

完整代码

分水岭_wz.cpp:

#include "watershed_wz.h"

WaterShed_WZ::WaterShed_WZ()





array<GVInt32, 6> WaterShed_WZ::getNeighbor_WZ(const GVInt32 idx, const GVInt32 width, const GVInt32 height, const GVInt32 thick) 

    GVInt32 SLICE = width * height;
    GVInt32 z = idx / SLICE;
    GVInt32 y = (idx%SLICE) / width;
    GVInt32 x = idx % width;

    array<GVInt32, 6> nIndex;
    nIndex[0] = (x == 0) ? -1 : (idx - 1);
    nIndex[1] = ((x + 1) == width) ? -1 : (idx + 1);
    nIndex[2] = (y == 0) ? -1 : (idx - width);
    nIndex[3] = ((y + 1) == height) ? -1 : (idx + width);
    nIndex[4] = (z == 0) ? -1 : (idx - SLICE);
    nIndex[5] = ((z + 1) == thick) ? -1 : (idx + SLICE);

    return nIndex;




void WaterShed_WZ::Watershed3D_WZ(
    const Mat im,
    const GVInt32 width,
    const GVInt32 height,
    const GVInt32 thick,
    GVInt32* label,
    const vector<vector<GVInt32>> marker)

    //<Parameter>
    //<image>  the image for watershed
    //<width>  the width of the image
    //<height> the height of the image
    //<thick>  the thick of the image
    //<label>  the map to save result. need to allocate memory before use watershed
    //<marker> the marker's index

//    const GVByte* image=im.data;

    auto t0 = chrono::high_resolution_clock::now();
    QTextStream out(stdout);


//    const GVInt32 SZ_slice = width * height;
//    const GVInt32 SZ = SZ_slice * thick;
    const GVInt32 markerNum = marker.size();

    // create toSearchList. Saved pixel connected to labeled pixels and wait to search
    vector<list<GVInt32>> toSearchList;
    toSearchList.resize(markerNum);

    // set label to INIT (unsearched)
//    ::memset(label, -1, sizeof(GVInt32) * SZ);

    // initialize
    array<GVInt32, 6> nIdx;
    for (size_t i = 0; i < markerNum; i++)
    
        for (GVInt32 idx : marker[i])
        
            // initialize label (which can be considered as a map of pointer to labelBar)
            label[idx] = i + 1;
            nIdx = getNeighbor_WZ(idx, width, height, thick);
            for (GVInt32 newidx : nIdx)
            
                if (newidx != -1)
                
                    if (label[newidx] == -1) 

                        toSearchList[i].push_back(newidx);

                        label[newidx] = -2;
                    
                
            
        
    

    //watershed
    GVByte h;
    GVInt32 idx;
    for (int h_cnt = 0; h_cnt < (1+(int)GV_BYTE_MAX); h_cnt++) // water height
    
        h = (GVByte)h_cnt;
        for (GVInt32 cnt = 0; cnt < markerNum; cnt++)  // for each marker

            list<GVInt32>::iterator it = toSearchList[cnt].begin();
            while (!toSearchList[cnt].empty())
            
                // for each pixel connected to the cnt-th labeled region

                idx = *it;
                // if this pixel is higher than water, ignore it
                if (im.at<unsigned char>(idx) > h)
                
                    ++it;
                    if(it == toSearchList[cnt].end())
                    
                        break;
                    
                    else
                    
                        continue;
                    
                
                // this pixel is lower than water, assign it
                label[idx] = cnt + 1;
//                L.at<int>(idx)=cnt + 1;

                // add new neighbor
                nIdx = getNeighbor_WZ(idx, width, height, thick);
                for (GVInt32 newidx : nIdx)
                
                    if (newidx != -1)
                    
                        if (label[newidx]== -1) 
                            toSearchList[cnt].push_back(newidx);
                            label[newidx] = -2;
//                            L.at<int>(newidx)=-2;
                        
                    
                
                // erase searched pixel
                it = toSearchList[cnt].erase(it);

                if(it == toSearchList[cnt].end())
                
                    break;
                
                else
                
                    continue;
                
            
        
    

    auto t1 = chrono::high_resolution_clock::now();
    auto dt = 1.e-9 * chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count();
    out << "Watershed used " << dt << " seconds.\n\n" << Qt::endl;

分水岭_wz.h:

#ifndef WATERSHED_WZ_H
#define WATERSHED_WZ_H

#define _USE_MATH_DEFINES

#include <vector>
#include <array>
#include <list>
#include <opencv2/core.hpp> //basic building blocks of opencv
#include <opencv2/imgcodecs.hpp> // image io
#include <opencv2/highgui.hpp> //image display
#include <QDebug>
#include <QTextStream>

#include <chrono>

using namespace std;
using namespace cv;

typedef unsigned char          GVByte;
typedef int32_t               GVInt32;
//typedef uint32_t               GVInt32U;

const GVByte              GV_BYTE_MAX = UCHAR_MAX;


class WaterShed_WZ

public:
    WaterShed_WZ();
    static array<GVInt32, 6> getNeighbor_WZ(const GVInt32 idx, const GVInt32 width, const GVInt32 height, const GVInt32 thick);
    static void Watershed3D_WZ(
            const Mat im,
            const GVInt32 width,
            const GVInt32 height,
            const GVInt32 thick,
            GVInt32* label,
            const vector<vector<GVInt32>> marker);
;

#endif // WATERSHED_WZ_H

【问题讨论】:

不看你的代码,我能看到的唯一问题是 cnt 可能不是一个有效的索引。 -g编译并在valgrind下运行。 valgrind 会准确地告诉您发生了哪些错误的内存操作、在哪一行以及错误访问的内存曾经是什么(在双重释放、释放后使用等情况下)。 【参考方案1】:

尝试像这样删除[cnt]索引

toSearchList[cnt].end() --> toSearchList.end()

【讨论】:

您好,Omer,感谢您的建议。我正在使用列表向量来保存要搜索的像素。你的意思是我实际上不能用list作为vector的元素? 我使用列表向量,因为我有几个标记,每个标记都需要有自己的待搜索像素列表。

以上是关于C++列表的内存损坏问题的主要内容,如果未能解决你的问题,请参考以下文章

内存损坏问题

挣扎 - 又一个内存损坏问题,错误分配(C++,VS 2008)

尝试读取或写入受保护的内存。这通常表明其他内存已损坏。在 C++ DLL 中

C++中数组的构造函数初始化列表

如何在 C++ 中分析和捕获双重删除和内存损坏

在 C++ 中访问列表向量的问题