为什么这个OpenCV代码会出现段错误?

mpe*_*kov 2 c++ opencv segmentation-fault

我准备并行化一些我的C++ OpenCV代码.我遇到了一个段错误,我无法理解发生了什么.

这是代码:

class ExhaustiveComparisonMT
{
    vector<Mat> *m_sigs;
    Mat *m_dist;
public:
    ExhaustiveComparisonMT(vector<Mat> sigs, Mat dist)
    {
        m_sigs = &sigs;
        m_dist = &dist; // gdb breakpoint 1 here
    }

    void operator() (size_t last_row, size_t last_col) const
    {
        Mat diff = (*m_sigs)[0].clone(); // segfault happens here, gdb breakpoint 2 here
        for (size_t i = 0; i != last_row; ++i)
        for (size_t j = 0; j != last_row; ++j)
        {
            cv::absdiff((*m_sigs)[i], (*m_sigs)[j], diff);
            m_dist->at<double>(i, j) = cv::sum(diff).val[0];
        }
    }
};

void
exhaustive_comparison(vector<Mat> sigs, Mat dist)
{
    size_t width = sigs.size();
    ExhaustiveComparisonMT ecmt(sigs, dist);
    ecmt(width, width);
}
Run Code Online (Sandbox Code Playgroud)

基本上,矩阵向量被传递给构造函数.指向向量的指针保留为成员变量,因此可以再次访问向量exhaustive_comparison.但是,该函数在尝试访问向量的第一个元素时会跳闸.

我试图通过放置两个断点来诊断gdb的问题(参见代码).在断点1:

(gdb) p (*m_sigs)[0]
$1 = (cv::Mat &) @0x7fffee77d010: {flags = 1124024325, dims = 2, rows = 1, cols = 712, data = 0x624ec0 "", refcount = 0x0, datastart = 0x624ec0 "", dataend = 0x6259e0 "", 
  datalimit = 0x6259e0 "", allocator = 0x0, size = {p = 0x7fffee77d018}, step = {p = 0x7fffee77d060, buf = {2848, 4}}}
Run Code Online (Sandbox Code Playgroud)

因此,正确访问第一个元素.现在,我们转到断点2并尝试相同的事情:

(gdb) p (*m_sigs)[0]
$2 = (cv::Mat &) @0x7fffee77d010: <error reading variable>
Run Code Online (Sandbox Code Playgroud)

第一个元素似乎不再可访问!它的地址是相同的(0x7fffee77d010).这里发生了什么?

最后,如果我向前迈进,我得到:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a0dd50 in cv::Mat::copyTo(cv::_OutputArray const&) const () from /usr/local/lib/libopencv_core.so.2.4
Run Code Online (Sandbox Code Playgroud)

OpenCV尝试访问第一个元素来克隆它,并失败.

为什么第一个元素在构造exhaustive_comparison函数中可以访问,但在成员函数中不可访问?

Nbr*_*r44 5

ExhaustiveComparisonMT(vector<Mat> sigs, Mat dist)
{
        m_sigs = &sigs;
        m_dist = &dist; // gdb breakpoint 1 here
}
Run Code Online (Sandbox Code Playgroud)

您传递的变量sigsdist副本 - 意味着,它们的生命周期仅限于此函数.

如果你试图获取它们的地址并在之后使用它,你将指向未知数据 - 因为在离开这个函数时,这些对象将被销毁,并且它们的地址将在以后被用于其他东西.

如果要将传递的对象的地址作为参数,则应使用引用 - 这样,您将实际检索原始对象的地址.当然,假设您将左值传递给函数并且在此期间它们不会被销毁.

ExhaustiveComparisonMT(vector<Mat> &sigs, Mat &dist)
{
        m_sigs = &sigs;
        m_dist = &dist; // gdb breakpoint 1 here
}
Run Code Online (Sandbox Code Playgroud)