我无法弄清楚以下崩溃的原因(MSVC9):
//// the following compiles to A.dll with release runtime linked dynamically
//A.h
class A {
__declspec(dllexport) std::string getString();
};
//A.cpp
#include "A.h"
std::string A::getString() {
return "I am a string.";
}
//// the following compiles to main.exe with debug runtime linked dynamically
#include "A.h"
int main() {
A a;
std::string s = a.getString();
return 0;
} // crash on exit
显然(?)这是由于可执行文件和DLL的内存模型不同。可能是字符串A::getString()
返回是在A.dll中分配并在main.exe中释放了吗?
如果是这样,为什么 - 以及在DLL(或可执行文件)之间传递字符串的安全方法是什么?不使用像shared_ptr这样的包装器和自定义删除器。
答案 0 :(得分:50)
这实际上不是由不同的堆实现引起的 - MSVC std :: string实现不为小的字符串使用动态分配的内存(它使用小字符串优化)。 CRT确实需要匹配,但这次不是你的想法。
发生的事情是您通过违反一个定义规则来调用未定义的行为。
发布和调试版本将设置不同的预处理器标志,您会发现std::string
在每种情况下都有不同的定义。询问你的编译器sizeof(std::string)
是什么 - MSVC10告诉我它在调试版本中是32,在发布版本中是28(这不是填充 - 28和32都是4字节`边界)。
那发生了什么事?使用复制构造函数的调试版本初始化变量s
以复制std::string
的发行版本。成员变量的偏移量在版本之间是不同的,因此您可以复制垃圾。 MSVC实现有效地存储了开始和结束指针 - 你已经将垃圾复制到它们中;因为它们不再为null,析构函数会尝试释放它们,并且您会获得访问冲突。
即使堆实现是相同的,它也会崩溃,因为你将垃圾指针释放到从未首先分配的内存中。
总结:CRT版本需要匹配,但定义 - 包括标准库中的定义。
答案 1 :(得分:3)
可能是字符串 A :: getString()返回 在A.dll中分配并释放 MAIN.EXE?
是。
如果是这样,为什么 - 以及什么是安全的 在DLL之间传递字符串的方法(或 可执行文件,就此而言)?没有 使用像shared_ptr这样的包装器 自定义删除。
使用shared_ptr
听起来对我来说是个明智之举。请记住,根据经验,分配和解除分配应该由同一个模块完成,以避免这些故障。
答案 2 :(得分:2)
您需要为应用程序中的每个DLL链接到相同的运行时库(DLL一个),无论是调试还是释放,其中内存在一个中分配,另一个在内存中释放。 (使用动态链接的运行时库的原因是,整个进程将有一个堆,而不是每个dll / exe链接到静态堆。)
这包括按值返回std :: string和stl-containers,就像你所做的那样。
原因有两个(更新部分):
因此,直接获取运行时库,或者停止在不同的dll中释放/分配(即停止按值传递内容)。
答案 3 :(得分:2)
除了上面所述之外,请确保两个项目中的平台工具集(在Properties-> General下)相同。否则,到达方的字符串内容可能是虚假的。
当使用v100工具集版本的控制台应用程序项目使用了设置为v90的库时,发生了这种情况。
答案 4 :(得分:1)
这可能是因为DLL和EXE是使用不同的CRT设置编译的。因此,当您传递字符串时,会发生一些资源冲突。检查DLL和可执行文件的项目设置。
答案 5 :(得分:0)
确保两个项目(App和Dll)都使用“ 多线程DLL ”运行时库之一,而不是静态版本。
属性-> C / C ++ -> 代码生成->( / MD或/ MDd )
注意: 如果您在应用中使用任何第三方库,则可能还需要重新编译它们,链接器通常会通过不匹配/重复的运行时错误通知您。