Re: Rép : XCode, cin and string (C++) => troubles :s
Re: Rép : XCode, cin and string (C++) => troubles :s
- Subject: Re: Rép : XCode, cin and string (C++) => troubles :s
- From: Andreas Grosam <email@hidden>
- Date: Mon, 15 Feb 2010 18:08:39 +0100
To all who are interested in this error:
The case:
---------
int main (int argc, char * const argv[]) {
string input = "";
getline(cin, input);
std::cout << "input: " << input << std::endl;
return 0;
}
The error:
----------
test(81379) malloc: *** error for object 0x1000062a0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Program received signal: “SIGABRT”.
The reason:
-----------
It turned out to be a coalescing issue with static variables.
Long Story:
-----------
A std::string is merely a pointer to an internal struct. This struct is a variable size struct. It contains fields for the reference count, the capacity, the length and finally a variable length array for the characters.
If the string will be default constructed or assigned an empty string, its internal pointer points to a static variable which actually constitutes the empty string. This empty_string variable exists only once. This is illustrated below:
class String {
static StringInternal empty_string = StringInternal();
StringInternal* data;
};
Note, that I'm using different names for the variables than the real case and also simplify the whole thing.
For all other strings not being empty, a raw memory will be allocated which size is at least sizeof(StringInternal) plus the number of characters (including zero termination) * sizeof (char). The first bytes is reserved for the StringInternal instance, the rest for the characters. The pointer to the internal data will be managed by the string class. The internal data may be shared with other string instances. When its reference count goes to zero, it will be deleted.
In order to prevent to delete the empty_string buffer (which has never been allocated) the string class makes a simple test:
if (this->data != &String::empty_string) {
if (data->ref_count<= 0)
... // delete it
}
So, normally this is no problem and it works fine. The problem starts with the shared lib of the C++ library. Normally, the C++ lib is "header-only" and consists of only template functions. When using such a library, everything will be instantiated on demand. However in order to reduce code bloat (which is a myth) the system provides us a shared lib of the C++ library, where a couple of function templates are "pre-instantiated". When compiling, the compiler is forced to not instantiate certain functions templates. When linking, the linker finds the missing functions in the shared lib.
In theory, this works.
However, for the compiler and linker, this is a very challenging task. Especially when they have to deal with templates, inline functions, specialized function templates, partially specialized class templates, static member templates and so force. Additionally, the linker flags must be set appropriately, not to accidentally hide global symbols in shared libs which must be visible to other dynamic shared objects. And finally, everything must work without issues, otherwise we may face subtle bugs - as it is the case here.
Here in this case, there is actually a problem with the static instance for the empty string. Since it is declared a static member of a class, it is a "global" variable (from the view point of the linker).
When the shared library will be created, there is no other way than to instantiate the static member in this library. That is, there exists actually one global visible variable String::empty_string within the shared lib.
When compiling the main.cpp module, the compiler may instantiate another one in this object file as well, but finally the linker must ensure that it will resolve the references to the address to the only one and the same variable, and this is the one in the shared lib.
And here is also the current problem: for some unknown reason, the linker does not resolve the address of the static member to the address of the instance within the shared lib. This is an error - and it leads to the specified error message.
When starting the program with this line:
std::string str;
the variable str will be initialized with a pointer to the empty_string static member. This variable is actually residing in the main DSO -- the main.cpp module.
When calling
std::getline(std::cin, input);
the program jumps to the shared lib DSO, since the getline function has been instantiated there, and does not exist in the main DSO. It has also been compiled a few years ago, and has its own static member String::empty_string.
So, the string will be modified in getline() and a new internal data variable is needed to hold the characters. For this, the string implementation allocates a new one, and tries to dealloc the old one. The code as mentioned before will be executed in order to check if member data points to the empty_string variable. But this time
if (this->data != &String::empty_string)
if (data->ref_count<= 0)
... // delete it
the first condition evaluates to TRUE, since now, the code compares to the address of the static member empty_string residing in its own DSO, the shared lib! There are two different String::empty_string instances!
Since, data->ref_count always returns zero if data points to the empty_string, the code tries to free the pointer which is actually a pointer to String::empty_string residing in the main DSO. And thus, we get this error.
So, why does the compiler/linker not resolve to the correct address?
For verifying if the symbol is visible at all in the shared library (libstd++.dylib), I used nm to figure this out:
The symbol actually is named _S_empty_rep_storage, so I searched for all symbols in libstdc++.dylib:
ag-macbookpro:Debug agrosam$ nm -m /usr/lib/libstdc++.6.0.9.dylib | grep _S_empty_rep_storage
0006fe50 (__DATA,__common) external __ZNSbIwSt11char_traitsIwESaIwEE4_Rep20_S_empty_rep_storageE
0006fdb0 (__DATA,__common) external __ZNSs4_Rep20_S_empty_rep_storageE
using c++filt in order to make it readable:
std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_Rep::_S_empty_rep_storage
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_S_empty_rep_storage
OK, these are the symbols we search for - and they are visible.
So, the questions remains, why the compiler/linker cannot resolve to these symbols when it compiles and links against the shared lib.
Thanks for reading :)
Regards
Andreas
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Xcode-users mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden