31

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 = {};?

Maximilian Gerhardt
  • 5,188
  • 3
  • 28
  • 61
  • 5
    I don't follow your expectation here. Obviously, none of the three points in the documentation page you link applies. – Baum mit Augen Feb 15 '18 at 14:48
  • 8
    `MyTest testObj = {}` is not zero initialization. It's value initialization, and it simply calls the default constructor, which in your case initializes nothing. – Igor Tandetnik Feb 15 '18 at 14:49
  • 5
    `MyTest() {}` constructor does not initialize anything. – Eljay Feb 15 '18 at 14:57
  • Is that behaviour specific to function pointers? – Ulrich Eckhardt Feb 15 '18 at 20:33
  • 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. – Martin York Feb 15 '18 at 21:57

1 Answers1

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.

halfelf
  • 9,737
  • 13
  • 54
  • 63
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 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 = {};` – Maximilian Gerhardt Feb 15 '18 at 14:55
  • Wait a sec. If no constructor was provided explicitly, all pointers should be zero? Or I'm wrong? – bartop Feb 15 '18 at 14:56
  • @bartop Had it the wrong way around, sorry, corrected. – Maximilian Gerhardt Feb 15 '18 at 14:57
  • 6
    @bartop you are wrong `int main(){int* p;}` - here `p` is not guaranteed to be `nullptr`. It is an uninitialized pointer with indeterminate value. – Jesper Juhl Feb 15 '18 at 15:00
  • @Vittorio: If you declare the default constructor with `= default`, would that cause it to be value initialized? – Nicol Bolas Feb 15 '18 at 15:54
  • @NicolBolas: seems like it will. I'll look for relevant (more precise) standard quotes – Vittorio Romeo Feb 15 '18 at 20:05
  • @NicolBolas: I think I found the right quotes, but would appreciate some feedback. – Vittorio Romeo Feb 15 '18 at 20:10
  • Which is this case means default-initialized which means the values will be indeterminate and [evaluating indeterminate values is UB](https://stackoverflow.com/q/23415661/1708801) so any value you obtain is fair game. – Shafik Yaghmour Feb 19 '18 at 07:35
  • @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! – Alexis Wilke May 08 '19 at 05:16
  • @VittorioRomeo No. It does not clear anything. It just defines the [default constructor](https://en.cppreference.com/w/cpp/language/default_constructor) and as we know, that does not set variable members to zero. – Alexis Wilke May 08 '19 at 05:18
  • @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`. – Vittorio Romeo May 08 '19 at 06:09
  • @AlexisWilke: see https://wandbox.org/permlink/sfrmhjzd3TbwHA99. I think you just misunderstood my answer. – Vittorio Romeo May 08 '19 at 06:11
  • @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(); } }` – Alexis Wilke May 08 '19 at 17:55
  • **"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-initialized*** – mada Dec 12 '21 at 07:51
  • @bartop *It is an uninitialized pointer with indeterminate value."* you're worng: it's is a **zero-initialized** null pointer of type int – mada Dec 12 '21 at 10:48