调试断言失败!表达式:_pFirstBlock == pHead

时间:2013-09-18 21:35:33

标签: c++ visual-studio-2012

我正在调用一个静态链接的.dll,我看到了这个错误:

enter image description here

我写了.dll和调用代码。不应该发生此错误。我想知道是否有其他人以前遇到过它? .dll只包含大约10行代码,它只是一个测试.dll来查看dll如何工作。当我将std :: string传回.dll时,它会爆炸。

我正在使用Visual Studio 2012和C ++。

接下来我会尝试

来自Debug assertion... _pFirstBlock == pHead

  

如果在a中使用单线程库,则可能会发生此问题   多线程模块。

明天,我将尝试在多线程模式下重新编译Boost静态库(我的.dll设置为多线程静态模式)。

接下来我会尝试

请参阅Using strings in an object exported from a DLL causes runtime error

  

你需要做两件事之一

     
      
  1. 使DLL和使用它的客户端都链接到CRT的 DLL 版本(例如,不是静态的)。
  2.   
  3. OR您需要确保不跨DLL边界传递动态分配的内存(例如字符串对象中包含的内存)。   换句话说,没有返回字符串的DLL导出函数   对象。
  4.         

这似乎与最新情况相符,它在我将字符串传回.dll边界的精确点上爆炸。只有在静态模式下链接所有内容时才会出现此问题。现在这是可以修复的。

请参阅Passing reference to STL vector over dll boundary

接下来我会尝试

请参阅Unable to pass std::wstring across DLL

解决方案

我有一个很好的解决方案,请参阅下面的答案。

4 个答案:

答案 0 :(得分:36)

在这种情况下,问题是我将std::string传递回.dll边界。

运行时库配置

  • 如果MSVC Runtime library设置为Multi-threaded Debug DLL (/MDd),那么这没问题(工作正常)。

  • 如果MSVC Runtime library设置为Multi-threaded Debug (/MTd),则会抛出此错误,可以使用以下说明修复此错误。

内存管理器A中分配的内存,并在内存管理器B中释放...

问题是内存是在.dll端分配的,然后在应用程序端释放相同的内存。这意味着内存管理器A正在分配内存,而内存管理器B正在释放相同的内存,这会产生错误。

解决方案是确保传回的所有内存都在DLL中分配 。换句话说,内存总是在应用程序端分配,并在应用程序端释放。

当然,DLL可以在内部分配/释放内存 - 但它无法分配稍后由应用程序释放的内存。

实施例

这将工作:

// Memory is allocated on the .dll side, and freed on the app side, which throws error.
DLL std::string GetString(); 

这将有效:

// Memory is allocated/freed on the application side, and never allocated in the .dll.
DLL int GetString(std::string& text); 

然而,这还不够。

在应用程序端,必须预先分配字符串:

std::string text("");
text.reserve(1024);     // Reserves 1024 bytes in the string "text".

在.dll端,必须将文本复制到原始缓冲区中(而不是用.dll端分配的内存覆盖):

text.assign("hello");

有时候,C ++会坚持分配内存。仔细检查预分配是否仍然是:

if (text.capacity < 1024)
{
   cout << "Memory was allocated on the .dll side. This will eventually throw an error.";
}

另一种方法是使用std::shared_ptr<std::string>,所以即使在.dll中分配了内存,它也会被.dll(而不是应用程序端)释放。

另一种方法是接受char *和长度,表示预先分配的内存量。如果我们想要传回的文本长于预分配内存的长度,则返回错误。

答案 1 :(得分:3)

当{em>表达式参数的计算结果为false时,这就是assert()的样子。此断言存在于C运行时库的Debug构建中,旨在检查分配问题。你的案例中的free()函数。 Debug构建添加额外的检查以确保您正确编写代码。并在发现问题时告诉您。就像在已经释放的分配上调用free()一样,这是一个简单的例子。或者调用free()传递错误的指针值,这是一个棘手的案例。或者当堆被早期代码破坏时调用free(),更难的情况。

这只是他们可以接受的,他们实际上并不知道为什么你的代码弄错了。例如,没有任何方法可以将大红色箭头放在破坏堆的代码上。 Debug + Windows + Call Stack调试器窗口涵盖了简单的案例,它将您带到程序中名为free()的代码。或std::operator delete用于C ++程序。更难的案例非常非常困难,堆腐败通常是Heisenbug。使断言可重复,以便您可以在报告的地址上设置数据断点是核心策略。为了这个简单的案例交叉手指,祝你好运!


编辑之后:是的,像std :: string这样的C ++类存在跨模块问题肯定是它可以捕获的问题之一。不是Heisenbug,有一个很好的问题。两个基本问题:

  • 每个模块都有自己的CRT副本,CRT的一个副本分配的对象不能被另一个CRT副本释放。它们每个都有自己分配的堆。在VS2012中解决的问题是,CRT现在从进程全局堆中分配。
  • 模块可能不使用std :: string的相同实现。对象布局不匹配。通过使用不同的C ++库版本编译模块轻松诱导,特别是C ++ 11更改的问题。或者不同的构建设置,_HAS_ITERATOR_DEBUGGING宏非常臭名昭着。

解决该问题的唯一方法是确保使用完全相同的构建设置,使用完全相同的编译器版本在程序中构建所有模块。使用/ MD是强制性的,它确保共享CRT,因此程序中只有一个。

答案 2 :(得分:2)

可能的原因:绑定到错误版本的Qt DLL,特别是在将项目从VS2010移动到VS2012时。

这是由于标准库的不同版本和相关的动态分配问题。

答案 3 :(得分:1)

重新安装Windows后我遇到了同样的问题。我的运行时库构建是多线程调试DLL(/MDd)。

我的解决方案是删除visual studio项目的*.user文件,删除调试文件夹并重建项目。