我有非常庞大的程序(> 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”。虽然“没有为任何堆栈帧加载符号”,但无法找到错误的位置。
我想这是一个非常严重的堆损坏案例,但是如何调试呢?我应该寻找什么?
感谢您的帮助。
答案 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中运行时,我遇到了一个多进程程序崩溃,但在从valgrind
或gdb
调用时运行得很完美。我发现(很多我的尴尬),我有一些相同程序的迷路进程仍在系统中运行,导致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;