MFC 程序中的堆损坏

Posted

技术标签:

【中文标题】MFC 程序中的堆损坏【英文标题】:Heap corruption in MFC program 【发布时间】:2012-08-28 07:34:00 【问题描述】:

我遇到了程序中的堆损坏问题。在程序中,我正在读取一个数据块并对其执行 FFT 和 IFFT。我正在为 2 个图像(主从图像)做这件事。完全相同的代码对主服务器运行良好,但在我尝试删除从属缓冲区时显示从属文件的堆损坏。

fcomplex 定义为:

typedef struct float real, imag;fcomplex;

附上相关部分代码的sn-p: 完整代码:http://sharetext.org/7xXe

如果我不为从映像调用 fft 和 ifft 函数,则不会发生错误。 (不过对于主人来说一切都很好)

为了调试错误,我安装了应用程序验证程序,但我无法解码日志文件。它在这里:http://sharetext.org/Y2ji(XML 文件复制粘贴)

视觉工作室给出的错误是: 检测到堆损坏:在 0x062C0040 的正常块 (#194456) 之后

CCoarseFun::fcomplex * slave_bfr;
CCoarseFun::fcomplex * slave_col;


slave_bfr = Pcoarse.init_1Dcmplx(SIZE*s_cols); 
slave_col = Pcoarse.init_1Dcmplx(SIZE);

Pcoarse.cfft1d_(&SIZE,slave_col,&FFTdir); // This function causes a problem
Pcoarse.complex_mult_col(filter, slave_col, SIZE, slave_col)
Pcoarse.cfft1d_(&SIZE,slave_col,&FFTdir); // As does this one

// delete memory related to slave
delete [] slave_bfr;    // Heap corruption here
delete [] slave_col;

让我感到困惑的是代码非常简单,而且它 100% 仅适用于主文件。为什么slave会崩溃?

有人可以指导我找到解决方案,或者也可以提供有关如何使用应用程序验证器的教程吗?

谢谢, 肖纳克

编辑:使用 Win7 x64 - VS2010

编辑 2:init_1Dcmplx 的定义

CCoarseFun::fcomplex* CCoarseFun::init_1Dcmplx(int n)

  fcomplex *a;
  a=new fcomplex[n];
  for(int i=0;i<n;i++)
  
    a[i].real=float(0.0);
    a[i].imag=float(0.0);
  
  return a;

EDIT3:cfft1D_ 的代码:http://sharetext.org/hzIg

EDIT4:mem.delfloat() 的代码

void CMemAlloc::del_float(float *a)

  if (a!=NULL)
  
    delete[] a;
    a=NULL;
  
  else
  
    return;
  

【问题讨论】:

init_1Dcmplx 是如何定义的?它是否使用new ...[] 来分配结构? @nneonneo 是的。请参阅 EDIT2 贴出cfft1d_的代码,相关部分。 @lyricat sharetext.org/hzIg 好吧,del_float 不太正确。它正在删除数组,但它并没有像看起来那样将指针设置回 NULL。那是因为它在指针的副本上进行操作。但是之后没有使用指针,所以我认为这不是最终原因。 【参考方案1】:

mem_float() 函数不正确。看起来它在删除后将指针设置为NULL,但它只是在指针的副本上工作,所以调用者的副本仍然指向已删除的内存块。

你可以这样做

delete [] cf;
cf = NULL;

你有几行看起来像这样:

four1(cf-1,nn,isign);

我认为这是在数组开始之前访问内存。

除此之外,four1() 内部的索引非常复杂 - 您必须使用调试器逐步检查它以检查边缘情况。

【讨论】:

我会调查它的行为。然而,对于主文件,这个函数在第一次调用时工作得很好。非常感谢您一直以来的帮助。 没关系,我对索引有误,我已经删除了那部分。 我想看看我可以对那个函数做些什么改变。不过我没有看到问题:该函数为 2*n 值初始化 cf [比如 32]。 “i”将一直到 30,因此索引 31 将有效。对 n 的约束是它必须是 2 的幂,因此代码将是有效的 IMO。编辑:对不起!没有看到你的更新 对不起!我在您发布编辑时输入了该内容!我会调查新问题。 F11'ing :) 改变这个太four1(cf,nn,isign) 会产生一个新的堆错误。但我认为这个解决方案是正确的,至少为我指明了正确的方向。我可能还不能调试 fft 代码。我可以通过某种方式解决它吗?【参考方案2】:

首先,了解堆损坏错误告诉您的内容很重要。当您在 Visual Studio 中运行调试版本时,它使用具有调试堆的运行时库的调试版本。每当您使用new 分配一些内存时,它的两侧都会有一些额外的保护字节。当您delete 时,调试堆会检查这些保护字节是否完整,如果没有,则会发出此警告。

假设Pcoarse.init_1Dcmplx() 在堆上分配内存,那么对它的两次调用很可能会一个接一个地分配内存:

XXXXXX - guard bytes
slave_bfr
XYXYXY - these are the guard bytes that are probably being corrupted
slave_cols
XXXXXX

您对slave_cols 执行了操作Pcoarse.cfft1d_(),但在删除slave_bfr 时出现堆损坏错误。这表明cfft1d_()slave_cols 开始之前覆盖了内存,因此它破坏了slave_bfr 的保护字节。

因此,我将查看您的 cfft1d_() 代码中可能存在负数组索引的位置,因为这可能会导致 slave_bfr 中的内存被踩踏。

还可以查看SO answers 的一些有用提示,了解如何充分利用调试堆,尤其是this one,了解如何启用更密集的内存检查。

【讨论】:

感谢您提供信息丰富的帖子。我正在尝试通过您在此页面上提供的链接和其他人的建议进行调试 即使我也简单地调用从属序列也会发生这种情况【参考方案3】:

“检测到堆损坏:在 0x062C0040 的正常块 (#194456) 之后”表示您分配了一个块,得到了地址为 0x062C0040 的块 #194456,并且您写入的内存字节比分配的多。所以你有一个经典的缓冲区溢出。您应该考虑用 STL 容器替换指针。在您的情况下,我更喜欢使用std::vector,而不是使用new float[] 分配的原始浮点数组。 STL 容器可以帮助您在错误访问时立即检测超出边界的写入,而不仅仅是在删除内存块之后。

【讨论】:

我个人本来希望这样做,因为我对指针/原始数组不太满意,但是所有旧代码都使用该结构,包括我需要调用的函数,例如 fft 等。也许我可以尝试分配一个更大的块 - 否则我的分配大小是错误的,因为代码对于 master 运行完美。 如果我用 slave_bfr = Pcoarse.init_1Dcmplx(SIZEs_cols+1) 替换 slave_bfr = Pcoarse.init_1Dcmplx(SIZEs_cols) 它不会给我任何错误。但是在删除语句之后我还有一个 30mb 的块。

以上是关于MFC 程序中的堆损坏的主要内容,如果未能解决你的问题,请参考以下文章

0xc0000374 未附加调试时 Cli/Cpp 代码中的堆损坏

混合模式 C++/CLI 崩溃:atexit 中的堆损坏(静态析构函数注册)

MFC 程序挂起:在 Vista 上更新 KB3059317 后 Comctl32.dll 损坏?

Visual Studio 2008. MFC 事件向导损坏

生成的 mfc 消息映射无效

修剪延迟的空闲队列时堆损坏