Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C++, does initializing a global variable with itself have undefined behaviour?

int i = i;  int main() {   int a = a;  return 0; }  

int a = a surely has undefined behaviour (UB), and more details on it is in Is reading an uninitialized value always an undefined behaviour? Or are there exceptions to it?.

But what about int i = i? In C++ we are allowed to assign nonconstant values to globals. i is declared and zero initialized (since it has file scope) before the declaration is encountered. In which case we are assigning 0 to it later in the definition. Is it safe to say this does not have UB?

like image 257
Dan Avatar asked Jun 15 '21 02:06

Dan


People also ask

Why does C have undefined behavior?

So, in C/C++ programming, undefined behavior means when the program fails to compile, or it may execute incorrectly, either crashes or generates incorrect results, or when it may fortuitously do exactly what the programmer intended.

What happens when variables are not properly initialized?

An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.

Why do we initialize variables to 0 in C?

In C programming language, the variables should be declared before a value is assigned to it. In an array, if fewer elements are used than the specified size of the array, then the remaining elements will be set by default to 0. Let us see another example to illustrate this.

What causes undefined behavior C++?

In C/C++ bitwise shifting a value by a number of bits which is either a negative number or is greater than or equal to the total number of bits in this value results in undefined behavior.


Video Answer


2 Answers

Surprisingly, this is not undefined behavior.

Static initialization [basic.start.static]

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized. If constant initialization is not performed, a variable with static storage duration or thread storage duration is zero-initialized. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before any dynamic initialization.

Important parts bold-faced. "Static initialization" includes global variable initialization, "static storage duration" includes global variables, and the above clause is applicable here:

int i = i; 

This is not constant-initialization. Therefore, zero-initialization is done according to the above clause (for basic integer types zero-initialization means, unsurprising, that it's set to 0). The above clause also specifies that zero initialization must take place before dynamic initialization.

So, what happens here:

  1. i is initialized to 0.
  2. i is then dynamically initialized, from itself, so it still remains 0.
like image 188
Sam Varshavchik Avatar answered Sep 23 '22 10:09

Sam Varshavchik


The behavior might be undefined for i, since depending on how you read the standard, you could be reading i before its lifetime starts.

[basic.life]/1.2

... The lifetime of an object of type T begins when:

— its initialization (if any) is complete ...

As mentioned in the other answer, i is initialized twice: first zero-initialized statically, then initialized with i dynamically.

Which initialization starts the lifetime? The first one or the final one?

The standard is being vague, and there are conflicting notes in it (albeit all of them are non-normative). Firstly, there is a footnote in [basic.life]/6 (thanks @eerorika) that explicitly says that the dynamic initialization starts the lifetime:

[basic.life]/6

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated26

...

26) For example, before the dynamic initialization of an object with static storage duration ...

This interpretation makes the most sense to me, because otherwise it would be legal to access class instances before they undergo dynamic initialization, before they could estabilish their invariants (including the standard library classes defined by the standard).

There's also a conflicting note in [basic.start.static]/3, but that one is older than the one I mentioned above.

like image 25
HolyBlackCat Avatar answered Sep 24 '22 10:09

HolyBlackCat