3

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?

5
  • "This is due to it being a native type" It is constant initialization (a part of static initialization), not restricted to "native types". Commented Apr 24, 2015 at 16:58
  • Simple types such as bool live in a fixed memory slot, which can be initialized at compile time. Complex objects such as a vector require run-time initialization. It's as simple as that. Commented Apr 24, 2015 at 16:59
  • 1
    Name your implementation. Commented Apr 24, 2015 at 16:59
  • If you're asking about "where memory lives" and "program boot", you need to pick a platform. The exact mechanism will vary. Commented Apr 24, 2015 at 17:00
  • Added, sorry about that its compiled with VS2012, x64 on PC (Windows) Commented Apr 24, 2015 at 17:01

3 Answers 3

2

That's because C++ standard distinguishes three types of initialization:

  • zero-initialization
  • constant initialization
  • dynamic initialization.

§ 3.6.2

  1. 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.

Sign up to request clarification or add additional context in comments.

9 Comments

Let me get this straight then, both the boolean and the vector are stored in the image and allocated when we boot. but depending on the 3 rules, the bool gets zero + constant initialization, and the vector dynamic initialization (a call to the constructor). so actually everything works as-is, apart from the vector getting a dynamic init. I do feel like the vector should be able to be const initialized however(since its a template class, everything i is known at compile time) Fun thought: this means if you call resize(1000) before the dynamic initialization happened. you have a giant mem leak.
First of all, you don't have a giant mem leak, you have an undefined behaviour. Because only way to do this before the dynamic initialization is to call resize from another static initializer (but which one?), which is not a valid logic (statics are considered valid after main() starts). Secondly - vector cannot be zero/static initialized, because it may involve dynamic memory management. Most non-POD types with user-defined constructors are dynamic-initialized.
@MateuszGrzejek statics are considered valid after main() starts May a static being invalid after the point of its definition? Besides, I think it may be possible for default-constructed vectors to be statically initialized. Just initializing their internal pointers to null.
@Lingxi We are entering the implementation-dependent ground. For me, it is safer to assume, that even non-complex constructors may cause a dynamic initialization. Any assumptions regarding non-local static variables may lead to painful errors.
@MateuszGrzejek Agreed. But as I remember it, the standard guarantees a valid static after its point of definition (within the same translation unit, of course), not after entering main.
|
1

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.

1 Comment

I have changed it to this yes, it is a good solution. the call to the function will force the dynamic/in-place construction of the static object. I won't pick this as a final answer, but I wanted to let you know the suggestion is very good to solve one of the problems.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.