Reputation: 3239
In my project, I use the constructor of a static object to collect pointers, like a registration method. Very simply, no magic. But during the start I experience a crash, and I can't explain what's happening here. The crash is reproducible on Windows with MSVC or Clang, both using the MSVC headers. Given is the following simple example. Can anyone give me a hint why this could cause issues?
This code seems to work just fine in GCC and Clang on Linux:
https://gcc.godbolt.org/z/vSKdpW
static int bar = 1;
static Registration abc(&bar);
static std::vector<void*> registrations;
void add_to_array(void* p)
{
registrations.push_back(p);
}
class Registration
{
public:
Registration(void* op)
{
add_to_array(op);
}
};
Executing results in the following crash (_Pnext was 0x8.)
void _Orphan_range(pointer _First, pointer _Last) const { // orphan iterators within specified (inclusive) range
#if _ITERATOR_DEBUG_LEVEL == 2
_Lockit _Lock(_LOCK_DEBUG);
_Iterator_base12** _Pnext = &_Mypair._Myval2._Myproxy->_Myfirstiter;
while (*_Pnext) { <======================= **_Pnext** was 0x8.
Does anyone know why a static vector can't be used to simply collect pointers to objects? foo.cpp
is the only file that uses the vector with push_back
. The array is not modified anywhere else.
Upvotes: 9
Views: 2206
Reputation: 485
Having debugged a similar issue just now using VS2019 runtime, I'll go ahead and say this is almost assuredly caused by a zero initialized (but not constructed) vector.
The forensics, in my case, looked like this: the value of _Pnext
was 0x8
because _Myproxy
is null. With _ITERATOR_DEBUG_LEVEL
set to 2 (i.e.: in Debug profile), _Myproxy
gets dereferenced, leading to this crash, but Release won't crash since that code is skipped altogether.
Now, looking at MSVC142/VS2019's implementation of vector, you'll see that every single constructor calls _Alloc_proxy
, which is what allocates some memory for _Myproxy
and assigns the value. So if the constructor had been invoked, you would've necessarily have a non-null _Myproxy
.
Static initialization in C++ occurs in two steps: zero initialization, followed by static initialization in an arbitrary, linker determined order. It appears that in the order you end up with, Foo.cpp's Registration
gets constructed before Bar.cpp's registrations
vector, effectively calling push_back
on a zero initialized (but not constructed) vector.
So as the comments suggested, you should definitely rearrange your static initialization so that your vector is properly constructed before using it.
(In my case, I was allocating memory for a struct containing a vector, but not calling placement operator new
on that memory. To anyone else finding this question like I did, look for any source of a zeroed but not constructed vector.)
Upvotes: 10