Do objects of built-in types have special static initialisation order precedence?
Asked Answered
S

4

6

I'd have expected the following code to yield a segmentation fault (or otherwise UB):

struct T {
   T();
};

T t;
char const* str = "Test string";

T::T() {
   std::cout << str; // zero-initialised, only!
}

int main() {}

That's because t is initialised before str. I'd expect str to hold the value (char const*)0 due to zero-initialisation. My interpretation of [C++11: 3.6.2/2] supports this.

However, the above snippet appears to output the string as expected (and I confirmed the behaviour by also printing the pointer's value).

Is there some rule of static initialisation that I'm missing here, that allows str to be value-initialised before t begins construction? Where is it in the standard?


This came up on static variable resolution at build time, where an answerer asserted that using char const* rather than std::string for a static global avoids the static initialisation order fiasco. I disagreed, but now I'm not so sure...

Stuck answered 5/1, 2012 at 21:55 Comment(2)
See https://mcmap.net/q/1775685/-using-a-free-quot-char-const-quot-at-static-initialization-time/34509 for a question that currently has an accepted but unfortunately wrong answer.Sherry
@JohannesSchaub-litb: Indeed.Stuck
S
1

I think I found it; what's happening here is not so much about the built-in type, but about the constant initialiser:

[C++11: 3.6.2/2]: Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) 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 (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
  • 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 (7.1.5), 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. [..]

That final sentence would seem to override subsequent sequencing rules, making this ordering apply across Translation Units.

Stuck answered 5/1, 2012 at 22:1 Comment(4)
Your last conclusion is incorrect. Only dynamic initialization can be "unordered" or "ordered". These two terms do not apply to static initialization. Order is no issue at all there.Sherry
@JohannesSchaub-litb: OK, how about the preceding two sentences, which repeat pretty much the same rule but without the word "unordered"?Stuck
they say "Static initialization shall be performed before any dynamic initialization takes place.". That's a hard requirement. There is no room for implementations to not follow that requirement.Sherry
@JohannesSchaub-litb: Hmm... I suppose.. will revisitStuck
S
6

str is initialized by a constant expression and const char * is a POD type (C++03 terms, but C++11 it is analogous, but with different terms and way more allowed cases). Such an initialization is done in static initialization phase, and the static initialization phase has no issue of order. It happens before any dynamic initialization. t is initialized in the dynamic initialization phase.

Sherry answered 5/1, 2012 at 22:0 Comment(4)
Is this covered by the passage I cited? Or somewhere else?Stuck
@Light yes you cited the c++11 text.Sherry
I'm not convinced that "str is initialized by a constant expression". It takes a string literal's address, which is certainly not a constant expression. Therefore str should have dynamic initialization. However implementations are permitted (3.6.2/3) to initialize it statically.Tzong
@Timo: I'm sure a string literal is a constant expression. All literals ought to be (5.19/2). That the pointer value may change between builds and invocations is not relevant.Stuck
E
1

Built-in types aren't initialised at all, in the normal sense. Commonly, their initial contents are memory-mapped directly from a special region of the binary as part of loading it.

Entertaining answered 5/1, 2012 at 22:0 Comment(3)
Turns out they are. That act is apparently known as "constant initialization".Stuck
@LightnessRacesinOrbit: Logically, yes. Physically, you'll find no code anywhere that says, "assign the address of this string-literal to str". That's what I meant by, "in the normal sense".Entertaining
nods Thanks. Was looking for standard reasoning though, so logic.Stuck
S
1

I think I found it; what's happening here is not so much about the built-in type, but about the constant initialiser:

[C++11: 3.6.2/2]: Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) 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 (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
  • 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 (7.1.5), 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. [..]

That final sentence would seem to override subsequent sequencing rules, making this ordering apply across Translation Units.

Stuck answered 5/1, 2012 at 22:1 Comment(4)
Your last conclusion is incorrect. Only dynamic initialization can be "unordered" or "ordered". These two terms do not apply to static initialization. Order is no issue at all there.Sherry
@JohannesSchaub-litb: OK, how about the preceding two sentences, which repeat pretty much the same rule but without the word "unordered"?Stuck
they say "Static initialization shall be performed before any dynamic initialization takes place.". That's a hard requirement. There is no room for implementations to not follow that requirement.Sherry
@JohannesSchaub-litb: Hmm... I suppose.. will revisitStuck
M
0
char const* str = "Test string";

is done by the compiler/linker, so it exists in its "initialized state" before the program even starts to run.

Mendicant answered 5/1, 2012 at 22:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.