Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this program crash: passing of std::string between DLLs

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.

like image 756
mats Avatar asked Feb 23 '10 22:02

mats


People also ask

What does std :: String () do?

std::string class in C++ C++ has in its definition a way to represent a sequence of characters as an object of the class. This class is called std:: string. String class stores the characters as a sequence of bytes with the functionality of allowing access to the single-byte character.

Does std :: string allocate?

The object str (it is the instance of the class std::string ) is allocated in the stack. However, the string data itself MAY BE allocated in the heap. It means the object has an internal pointer to a buffer that contains the actual string.

Why do I need std :: string?

Because the declaration of class string is in the namespace std. Thus you either need to always access it via std::string (then you don't need to have using) or do it as you did.


1 Answers

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::string has a different definition in each case. Ask your compiler what sizeof(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 s is initialized using the debug version of the copy constructor to copy a release version of std::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.

like image 97
JoeG Avatar answered Oct 13 '22 11:10

JoeG