程序仅在调试器外部的发布模式下崩溃

时间:2010-02-27 13:59:10

标签: c++ memory crash heap

我有非常庞大的程序(> 10k行的C ++代码)。当从Visual Studio中启动时,它在调试模式或发布模式下完美运行,但是从命令行手动启动时,释放模式二进制文件通常会崩溃。(不总是!!!)。

带删除的行会导致崩溃:

bool Save(const short* data, unsigned int width, unsigned int height, 
          const wstring* implicit_path, const wstring* name = NULL, 
          bool enable_overlay = false)
{
    char* buf = new char[17];
    delete [] buf;
}

编辑:根据请求扩展了示例。

在我的测试用例中,“len”长度为16。没关系,如果我对buf做了一些事情,它会在删除时崩溃。

编辑:没有delete []行,应用程序工作正常,但我认为它会泄漏内存(因为块永远不会被分配)。删除行后从未使用的buf。它似乎也不会与除char之外的任何其他类型崩溃。现在我真的很困惑。

崩溃消息非常不明确(典型的Windows“xyz.exe已停止工作”)。当我单击“调试程序”选项时,它进入VS,其中错误被指定为“访问冲突写入位置xxxxxxxx”。虽然“没有为任何堆栈帧加载符号”,但无法找到错误的位置。

我想这是一个非常严重的堆损坏案例,但是如何调试呢?我应该寻找什么?

感谢您的帮助。

9 个答案:

答案 0 :(得分:11)

你有没有检查其他地方的内存泄漏?

通常奇怪的删除行为是由于堆在某一点上被破坏引起的,然后很久以后,由于另一个堆的使用而变得明显。

调试和发布之间的区别可能是由Windows在每个上下文中分配堆的方式引起的。例如,在调试中,堆可能非常稀疏,并且损坏不会立即影响任何内容。

答案 1 :(得分:5)

在调试器中启动并自行启动之间的最大区别是,当从调试器中启动应用程序时,Windows提供了一个“调试堆”,它填充了0xBAADF00D模式;请注意,这不是CRT提供的调试堆,而是填充了0xCD模式(IIRC)。

Here是微软针对此功能提及的少数提及之一,here您可以找到有关此功能的链接。

该链接中还提到“启动程序并使用调试器附加到程序 NOT 使其使用”特殊调试堆“。”

答案 2 :(得分:2)

你可能在某处有内存覆盖,而delete []只是第一次出现问题。但覆盖本身可以位于程序的完全不同的部分。困难在于找到覆盖。

添加以下功能

#include <malloc.h>

#define CHKHEAP()  (check_heap(__FILE__, __LINE__))

void check_heap(char *file, int line)
{
    static char *lastOkFile = "here";
    static int lastOkLine = 0;
    static int heapOK = 1;

    if (!heapOK) return;

    if (_heapchk() == _HEAPOK)
    {
        lastOkFile = file;
        lastOkLine = line;
       return;
    }

    heapOK = 0;
    printf("Heap corruption detected at %s (%d)\n", file, line);
    printf("Last OK at %s (%d)\n", lastOkFile, lastOkLine);
}

现在经常在整个程序中调用CHKHEAP()并再次运行。它应该显示源文件和堆损坏的行以及最后一次它可以的位置。

答案 3 :(得分:1)

崩溃有很多可能的原因。找到它们总是很困难,特别是当它们从调试模式到发布模式不同时。

另一方面,由于您使用的是C++,因此您可以使用std::string代替手动分配的缓冲区&gt;&gt;有RAII存在的原因;)

答案 4 :(得分:0)

  

这两个是其功能中的前两行。

如果你的意思是我解释它的方式,那么第一行是在一个函数中声明一个局部变量buf,但删除是删除在第二个函数之外声明的一些不同的buf。

也许你应该展示这两个功能。

答案 5 :(得分:0)

您是否曾尝试使用相同的构建文件将其隔离,但仅基于您上面的内容编写代码?类似的东西:

int main(int argc, char* argv[] )
{
    const int len( 16 );
    char* buf = new char[len + 1]; 

    delete [] buf;
}

您提供的代码绝对正常,并且在它自己的代码中,应该在调试或优化时运行没有任何问题。因此,如果问题不在于您的代码的具体细节,那么它必须归结为项目的细节(即编译/链接)

您是否尝试过创建一个全新的项目并将10K + C ++行放入其中?可能不需要太长时间来证明这一点。特别是如果现有项目已被导入或大量改变。

答案 6 :(得分:0)

听起来你在代码的某个地方有一个整体变量。

在调试模式下,所有内存都被初始化为标准,因此您将获得一致的行为。

在发布模式下,除非您明确做某事,否则不会初始化内存。

使用最高级别设置的警告运行编译器。
然后确保代码编译没有警告。

答案 7 :(得分:0)

我遇到了同样的问题,我发现当我去删除字符串长度为1的[]字符串指针时,我的程序才崩溃。

void DeleteCharArray(char* array){
 if(strlen(array)>1){delete [] array;}
 else{delete array;}
}

这解决了这个问题,但它仍然容易出错,但可以修改为其他方式。 无论如何我发生这种情况的原因我怀疑是C ++ char * str = new char [1] char * str = new char; 是一回事,这意味着当你试图删除一个只有数组的 delete [] 的指针时,结果是意外的,而且往往是致命的。

答案 8 :(得分:0)

当我观察到这种症状时,我遇到的一种问题是,当我在shell中运行时,我遇到了一个多进程程序崩溃,但在从valgrindgdb调用时运行得很完美。我发现(很多我的尴尬),我有一些相同程序的迷路进程仍在系统中运行,导致mq_send()调用返回错误。问题是那些杂散进程也被内核/系统分配了消息队列句柄,因此我新生成的进程中的mq_send()失败了,但是不确定(根据内核调度情况)。

就像我说的那样,琐碎,但是直到你发现它,你才会撕掉你的头发!

我从这个艰难的课程中学到了,这些天我的Makefile拥有所有适当的命令来创建新的构建,并清理旧的环境(包括拆除旧的消息队列和共享内存和信号量等) 。通过这种方式,我不会忘记做一些事情,并且必须对看似困难(但显然是可以解决的)问题感到焦虑。这是我最新项目的剪切和粘贴:

[Makefile]
all:
      ...
...

obj:
      ...
clean:
      ...
prep:
  @echo "\n!! ATTENTION !!!\n\n"
  @echo "First: Create and mount mqueues onto /dev/mqueue (Change for non ubuntu)"
  rm -rf /run/shm/*Pool /run/shm/sem.*;
  rm -rf /dev/mqueue/Test;
  rm -rf /dev/mqueue/*Task;
  killall multiProcessProject || true;