我有一个如下原型的函数:
void function(std::string str);
在我的main函数中,在另一个加载和使用该dll的程序中调用此函数。
function("some string value here");
从此函数返回时,我收到堆损坏错误:
Windows在program.exe中触发了断点。
这可能是由于堆的损坏,这表明存在错误 program.exe或它加载的任何DLL。
这也可能是由于用户在按下F12时 program.exe具有焦点。
输出窗口可能包含更多诊断信息。
玩弄我的代码我发现了一些奇怪的意见:
1.当传入的字符串长度小于11个字符时,我没有错误,只要我添加更多字符就会出现错误。
2.将参数类型从std::string
更改为std::string&
时,错误消失。传递参考的想法来自here
我已经评论了这个功能的主体。那里的操作与产生的例外无关
4.将参数类型从std::string
更改为char*
也可以解决问题
可能导致此错误的原因是什么?我该如何解决?
答案 0 :(得分:8)
最有可能的是,由于在Windows中,DLL拥有自己的私有堆这一事实,您会看到崩溃。
编译函数时,编译器为std::string
的析构函数生成了一些代码,以清理它的参数。此代码释放DLL堆上分配的内存。但是,应用程序EXE还为std::string
的构造函数生成自己的代码,该代码在程序堆上分配代码。当您在一个堆上分配并在另一个堆上释放时,会发生未定义的行为,并且您将崩溃。
至于为什么小字符串不会触发错误 - 许多std::string
实现将小字符串内联到结构本身,以避免堆开销。当你的字符串足够小以适应时,不需要进行内存分配,因此它似乎正常工作......只要你对EXE和DLL使用相同的STL版本,并且内联的阈值永远不会改变。
要避免此问题,请不要按值将对象传递给DLL(除非它们是POD objects),并且不要释放不同于其创建的DLL或EXE中的对象。避免传递STL或C ++库对象,因为它们的实现可能在C ++编译器的不同版本之间有所不同。传递POD对象或C基元类型,例如const char *
。
答案 1 :(得分:6)
导出DLL函数时,最好只接受整数数据类型,即int或指针(不确定float和double)。
当你需要传递一个字符串时,将它作为const char *
传递,当你需要DLL函数返回一个字符串时,向{DLL}传递指向预分配缓冲区的char *
指针, DLL将写入字符串。
永远不要在DLL自己的函数之外使用DLL分配的内存,也不要传递具有自己的构造函数/析构函数的值结构。
答案 2 :(得分:5)
可能您已经与C运行时的静态版本链接,创建与C运行时的静态版本链接的DLL永远不是一个好主意。这可能会导致许多问题,例如在您的程序中,您的EXE从与其链接的静态C运行库的私有堆中分配内存,然后在您的DLL中要删除该堆并创建新堆(因为您要添加输入字符串的一些数据,它需要增加其缓冲区),因此会导致错误。最简单的方法是将程序的所有部分(EXE和DLL)与C ++运行时的DLL版本链接起来,这样它们都可以从MSVCRTXX.dll共享相同的堆