发布模式下 OpenCV 堆损坏

Aut*_*ass 1 c++ opencv visual-studio-2010

我有一个使用 openCV 2.4.10 在 Visual Studio 2010 中编写的简单代码,它从某些输入文件中提取一些功能。

Mat extractSIFT(Mat img)
{
    cv::Ptr<cv::FeatureDetector> detector;
    cv::Ptr<cv::Feature2D> descriptorExtractor;

    detector = cv::FeatureDetector::create("Dense");
    descriptorExtractor = cv::DescriptorExtractor::create("SIFT");

    detector->set("initXyStep",GRID_SPACING);

    vector<cv::KeyPoint> keypoints;
    detector->detect(img,keypoints);

    Mat o;
    //Mat o(keypoints.size(),128,CV_8U);
    descriptorExtractor->compute(img,keypoints,o);

    return o;
}
Run Code Online (Sandbox Code Playgroud)

尽管此代码在调试模式下工作正常(尽管很慢),但它给出了以下错误:

Windows 在 Prototype.exe 中触发了断点。

这可能是由于堆损坏造成的,这表明 Prototype.exe 或其加载的任何 DLL 中存在错误。

这也可能是由于用户在 Prototype.exe 获得焦点时按了 F12。

输出窗口可能有更多诊断信息。

经过进一步调查,我发现输出变量 o 在发布模式(悬停在上方)下看不到,但可以在简单的控制台转储上打印值。

在反汇编程序中:

   848:     //Mat o(keypoints.size(),128,CV_8U);
   849:     descriptorExtractor->compute(img,keypoints,o);
   850: 
   851:     return o;
013F6FCF 56                   push        esi  
013F6FD0 8D 55 C0             lea         edx,[keypoints]  
013F6FD3 52                   push        edx  
013F6FD4 8D 45 0C             lea         eax,[img]  
013F6FD7 50                   push        eax  
013F6FD8 8B CF                mov         ecx,edi  
013F6FDA C7 45 F0 01 00 00 00 mov         dword ptr [ebp-10h],1  
013F6FE1 E8 42 35 02 00       call        cv::Feature2D::compute (141A528h)  
013F6FE6 8B 45 C0             mov         eax,dword ptr [keypoints]  
013F6FE9 3B C3                cmp         eax,ebx  
013F6FEB 74 09                je          extractSIFT4+306h (13F6FF6h)  
013F6FED 50                   push        eax  
**013F6FEE E8 EC 5C 02 00       call        operator delete (141CCDFh)**  
013F6FF3 83 C4 04             add         esp,4  
Run Code Online (Sandbox Code Playgroud)

错误发生与星号一致。我尝试了几个项目属性(/Md,MT,增量构建,...)重新编译openCV,检查平台版本(v100)但无济于事。

Cod*_*ard 5

对于任何类型的堆损坏,Microsoft Application Verifier(免费软件)都是无价的。您需要使用 Basics\Heaps 检查来配置它,我建议第一次禁用所有其他检查。保存设置后,您将需要重新启动程序。它会在腐败点崩溃。

例如:假设您在堆上分配了 100 个字节,然后尝试写入 101 个字节。在调试版本中,C++ CRT 将在块之前和之后添加一些额外的填充,这将防止和检测小堆损坏。在发布版本中,没有填充并且堆被损坏,您通常在进行其他堆操作时才意识到这一点为时已晚。使用应用程序验证器,您的程序将在写入第 101 个字节时准确地崩溃到调试器中。当发生这种情况时,您可以在 Visual Studio 的输出窗口中看到一些额外的详细信息。

(注意:我总是为我开发的程序启用带有 Basics\Heaps 的应用程序验证器)

在这种特定情况下,您可能会看到向量的析构函数导致崩溃。这意味着没有发生堆损坏,但程序正在尝试删除不存在的堆块。这很可能是由于链接了使用不同编译器或不同调试/发布设置编译的 OpenCV。类的结构vector和分配的块填充在编译器和调试/发布之间有所不同。

更新: 既然我们知道了不同 CRT 的情况,这里有一个扩展的解释。

  • 矢量在 OpenCV 中分配并在您的程序中释放。
  • 如果您有不同版本的 CRT(编译器、调试/发布)
    • 分配和释放逻辑可能完全不同
    • 你的程序很可能会崩溃。
  • 如果您有相同版本的 CRT
    • CRT(C 运行时库)通过 Windows 堆处理内存分配。
    • 每个堆都有自己的句柄。
    • CRT(直到 VS2012 或类似的东西)将创建自己的堆用于分配。
    • 如果您有两个不同的 CRT,则尝试释放其他 CRT 分配的块将使用错误的堆句柄,这将导致崩溃或泄漏内存
    • 我猜,你的程序在静态库中使用 CRT,而 OpenCV 在 DLL 中使用 CRT。
    • 静态链接 CRT 意味着嵌入私有 CRT。
    • 因此,您有两个 CRT:一个在您的程序中,一个在 OpenCV 使用的 DLL 中。
    • 现在您将程序更改为在 DLL 中使用 CRT,它们都使用相同的 DLL,每个进程仅加载一次,因此您现在只能使用 CRT。