I have some trouble figuring out why the following crashes (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
Obviously (?) this is due to the different memory models for the executable and DLL. Could it be that the string A::getString() returns is being allocated in A.dll and freed in main.exe?
If so, why – and what would be a safe way to pass strings between DLLs (or executables, for that matter)? Without using wrappers like shared_ptr with a custom deleter.
This isn’t actually being caused by differing heap implementations – the MSVC std::string implementation doesn’t use dynamically allocated memory for strings that small (it uses the small string optimization). The CRTs do need to match, but that isn’t what bit you this time.
What’s happening is that you’re invoking undefined behaviour by violating the One Definition Rule.
The release and debug builds will have different preprocessor flags set, and you’ll find that
std::stringhas a different definition in each case. Ask your compiler whatsizeof(std::string)is – MSVC10 tells me that it’s 32 in a debug build and 28 in a release build (this isn’t padding – 28 and 32 are both 4 bytes` boundaries).So what’s happening? Variable
sis initialized using the debug version of the copy constructor to copy a release version ofstd::string. The offsets of the member variables are different between the versions, so you copy garbage. The MSVC implementation effectively stores begin and end pointers – you’ve copied garbage into them; because they’re no longer null, the destructor tries to free them and you get an access violation.Even if the heap implementations were the same it would crash, as you’re freeing garbage pointers to memory that was never allocated in the first place.
In summary: the CRT versions need to match but so do the definitions – including the definitions in the standard library.