Reputation: 397
I know there is a lot about what is a static variable and about the initialization order, this goes a bit further.
Imagine in 1 CPP, the following lines:
struct A
{
void* a;
size_t b;
};
static bool bMyBoolean = true;
static std::vector<A> myVector;
The guarantee here is that bMyBoolean
is initialized before myVector
.
They also are both initialized before main()
gets called.
However, myVector
has a valid value and memory address, but gets initialized during _initterm while bMyboolean
is initialized earlier and doesn't need an initialize call during initterm.
This is due to it being a native type it seems, but I can't find any references or info about this behavior.
A bit of context: when I overload malloc
for example, and a constructor is called for a userdefined type, it will go into malloc
, but some of your static data here is not ready yet (this is expected since static variables between translation units is not guaranteed) but it means I can access and alter it safely, and then it gets re-initialized.
This leads me to the next question, where does this memory live then?
Does it get in-place reconstructed?
Since a malloc
call is being made, it then gets initialized. Does it mean the native variables live in a static heap that is allocated at boot of the program, and the user defined types live on the heap?
If that is the case, how can you track your user defined types that are declared static?
Upvotes: 3
Views: 882
Reputation: 206707
You've got four questions in your post. However, I have a hunch that your primary concern is that the states of some of the static
objects are modified by malloc
before those objects are initialized by the run time environment.
It's better not to rely on the initialization order of global static
variables.
If malloc
needs to modify some static
data, it's better to provide access to those data through a function.
Instead of:
static std::vector<A> myVector;
use
static std::vector<A>& getVector()
{
static std::vector<A> myVector;
return myVector;
}
If you do that, myVector
is guaranteed to be initialized when the function returns.
Upvotes: 1
Reputation: 14987
bMyBoolean
and myVector
both live in static storage, contributing a total of sizeof(bool) + sizeof(std::vector<A>)
bytes. Static storage is in the image file itself, say app.exe
. When you execute app.exe
, Windows loads (maps) the image file into the virtual memory address spawned for the new process, and that is when the originally "dead" static storage comes into alive. The system later runs the code in app.exe
, which involves invoking the constructor of std::vector<A>
on myVector
. That is, on the static storage of myVector
. Remember the this
pointer? The this
pointer passed to the constructor will point to the static storage of myVector
. The memory allocated by the constructor is then dynamic and on the heap. Pointer to this dynamic storage is stored in the static storage of myVector
.
I suggest you to read more about linking and loading.
Upvotes: 0
Reputation: 12088
That's because C++ standard distinguishes three types of initialization:
§ 3.6.2
Variables with static storage duration or thread storage duration shall be zero-initialized before any other initialization takes place.
Constant initialization is performed:
- if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression and the reference is bound to an lvalue designating an object with static storage duration or to a temporary
- if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution, every constructor call and full-expression in the mem-initializers and in the brace-or-equal initializers for non-static data members is a constant expression
- if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.
Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.
What are you talking about is constant initialization (a part of static initialization) and it is not restricted to built-in types.
Get familiar with section 3.6.2 of the standard: "Initialization of non-local variables" if you want to know more.
[...] but it means I can access and alter it safely, and then it gets re-initialized.
Of course - you can always modify content under specific memory location as long as OS allows you to do so. Think about it like placement new - there is an allocated memory for such object, but constructor gets called when it comes to initialization phase. So it is initialized "in place".
Does it mean the native variables live in a static heap that is allocated at boot of the program, and the user defined types live on the heap?
No. There is no direct connection between type of variable and its location. Local variables are placed on the stack, dynamic (allocated via malloc()
/new
) live inside the heap and static are placed inside the image (for example MyApp.exe
). After app is executed, it is loaded to memory, including all objects with static storage, for which memory is reserved at this point. You may consider such objects valid as long as main()
is running.
Upvotes: 2