C++ Zero-Initialization
Asked Answered
H

1

32

I'm having trouble understanding when and why exactly a member in my class is zero-initialized according to http://en.cppreference.com/w/cpp/language/zero_initialization.

Consider the following test program:

#include <iostream>
#include <stdio.h>

class MyTest {
private:
    const static unsigned int dimension = 8;
    void (* myFunctions [dimension])();

public: 
    MyTest() {}

    void print() { 
        for(unsigned int i=0; i < MyTest::dimension; i++) {
            printf("myFunctions[%d] = %p\n", i, this->myFunctions[i]);
        }   
    }
};


int main() {
    //We declare and initialize an object on the stack 
    MyTest testObj = {};
    testObj.print();

    return 0;
}

I am declaring a class to have an array of 8 function pointers of the signature "void functionname()". When I declare and initialize an object of the class in main as MyTest testObj = {}; or MyTest testObj;, I expected it to be zero-initialized, i.e. all pointers are null pointers.

However, compiling with g++ 5.3.0 on my Windows 10 machine with g++ -m32 -o test -std=c++14 test.cpp && test machine gives the output:

myFunctions[0] = 76dd6b7d
myFunctions[1] = 00401950
myFunctions[2] = 0061ff94
myFunctions[3] = 004019ab
myFunctions[4] = 00401950
myFunctions[5] = 00000000
myFunctions[6] = 003cf000
myFunctions[7] = 00400080

Which look like un-initialized values from the stack..

If I move the declaration of the object outside of main (as a global variable), it prints all zeroes again.

If I have understood cppreference correctly, this is because I have a variariable with static storage duration, and is thus zero-initialized. It initializes my class type by zero-initializing all non-static data members of my class (i.e., the myFunctions) array. An array is initialized by zero-initializing every element of it, which, in my function pointer case, is a null pointer.

Why does it not zero-initialize my object the stack when I declare it with MyTest testObj = {};?

Hill answered 15/2, 2018 at 14:44 Comment(5)
I don't follow your expectation here. Obviously, none of the three points in the documentation page you link applies.Recycle
MyTest testObj = {} is not zero initialization. It's value initialization, and it simply calls the default constructor, which in your case initializes nothing.Ferwerda
MyTest() {} constructor does not initialize anything.Donielle
Is that behaviour specific to function pointers?Leiva
Note: 6.6.2 Static initialization An object of static storage duration is zero initialized. That is why when you make it a global variable everything is initialized to zero. Automatic objects follow slightly more complex rules.Phylloid
I
43

The following

MyTest testObj = {};

is not zero-initialization for MyTest, but is simply calling its default constructor. The cppreference page explains why (emphasis mine):

As part of value-initialization sequence for non-class types and for members of value-initialized class types that have no constructors, including value initialization of elements of aggregates for which no initializers are provided.

MyTest is a class type, and a has a constructor.


Defining the constructor with

MyTest() = default;

will instead zero-initialize the object.

Relevant Standard quotes (emphasis mine) below.

From [dcl.init#8]:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;

  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;

  • ...

From [dcl.init.list]:

List-initialization of an object or reference of type T is defined as follows:

  • ...

  • Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.

Improbity answered 15/2, 2018 at 14:49 Comment(15)
Ah, thank you. Together with the value-initialization part I now also understand why, when I comment out the constructor in the class, it prints the same uninitialized values when declared as MyTest testObj ; and all-zero when I do MyTest testObj = {};Hill
Wait a sec. If no constructor was provided explicitly, all pointers should be zero? Or I'm wrong?Frangipane
@Frangipane Had it the wrong way around, sorry, corrected.Hill
@Frangipane you are wrong int main(){int* p;} - here p is not guaranteed to be nullptr. It is an uninitialized pointer with indeterminate value.Cretaceous
@Vittorio: If you declare the default constructor with = default, would that cause it to be value initialized?Missymist
@NicolBolas: seems like it will. I'll look for relevant (more precise) standard quotesImprobity
@NicolBolas: I think I found the right quotes, but would appreciate some feedback.Improbity
Which is this case means default-initialized which means the values will be indeterminate and evaluating indeterminate values is UB so any value you obtain is fair game.Magritte
@NicolBolas, yeah, I'm not too sure why he says that the default constructor would somehow help. It does in many circumstances but having such a definition doesn't set variables to 0. Personally, I now set all my variables (except references) to a default value in all my classes. I can avoid having to declare constructors altogether! i.e. class A { ... int member_variable = -1; ... };. That's the ONE feature I wanted in C++. So I'm happy now!Bellringer
@VittorioRomeo No. It does not clear anything. It just defines the default constructor and as we know, that does not set variable members to zero.Bellringer
@AlexisWilke: I will have to review this, but I think that I meant that struct X { int i; X() = default; }; int main() { X x{}; } will guarantee that x.i == 0.Improbity
@AlexisWilke: see wandbox.org/permlink/sfrmhjzd3TbwHA99. I think you just misunderstood my answer.Improbity
@VittorioRomeo, oh! I tested again and it actually works!? However, your test would need a function which makes the stack dirty otherwise you don't have a good test that proves anything. Like this: void make_stack_dirty() { int stack[16]; for(size_t idx(0); idx < 16; ++idx) { stack[idx] = rand(); } }Bellringer
"if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized" So why MyTest testObj = {}; is said value-initializedDetour
@Frangipane It is an uninitialized pointer with indeterminate value." you're worng: it's is a zero-initialized null pointer of type intDetour

© 2022 - 2024 — McMap. All rights reserved.