4

I cannot understand the behavior of gcc 4.8.1 or Visual Studio 2015 with respect to default initialization versus value initialization.

It doesn't help that I'm trying to understand the differences between these myself and possibly running into compiler bugs?

My question is: Can someone explain this behavior? And ideally tell me what should be happening.

I have two classes:

class Foo{
    int _bar;
public:
    void printBar(){ cout << _bar << endl; }
};

class bar{
    int ent;
public:
    int getEnt(){return ent;}
};

I'm using the following code to test:

int main()
{
    Foo foo;

    foo.printBar();
    Foo().printBar();

    bar b;

    cout << b.getEnt() << endl;

    return 0;
}

On gcc and Visual Studio I get:

134514795
0
0

Now if I change the test code to:

int main()
{
    Foo foo;

    foo.printBar();

    bar b;

    cout << b.getEnt() << endl;

    return 0;
}

gcc gives me:

0
0

And Visual Studio gives me:

50790236
51005888

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • If anyone is interested I started looking at this in response to: http://stackoverflow.com/a/27443703/2642059 – Jonathan Mee Dec 15 '14 at 12:49
  • Isn't this question the same? – Anton Savin Dec 15 '14 at 12:53
  • @AntonSavin I really just want to know what the compiler should be doing with initialization. I don't *think* that's the same question? – Jonathan Mee Dec 15 '14 at 13:00
  • With debug build, usually a filler is used to help detect uninitialized values... and other bytes are used to help detect some other memory related problem (like memory already freed). – Phil1970 Jul 14 '17 at 16:16

4 Answers4

9

Default initialisation, of classes like this without user-defined constructors, does nothing, leaving each trivial member with an indeterminate value.

Value initialisation will zero-initialise each member.

In the first case, you're printing:

  • the indeterminate value of a default-initialised Foo foo;
  • the zero value of a value-initialised Foo()
  • the indeterminate value of a default-initialised bar b;

The third one happens to be zero; perhaps because it reuses the storage of the temporary value-initialised Foo.

In the second case, you're printing the indeterminate values of two default-initialised objects. Coincidentally, they have zero values in one case but not the other.

Both programs have undefined behaviour, since they use uninitialised values.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
5

The logic is quite simple:

  1. Default initialization of a class just default initializes all members.
  2. Default initialization of built-in types leaves member uninitialized.
  3. Accessing an uninitialized object yields undefined behavior.
  4. Undefined behavior can do anything it wants.
  5. Both compilers provide "correct" results. Note that causing nasal demons to be emitted would also be correct.
plasmacel
  • 8,183
  • 7
  • 53
  • 101
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
3
Foo foo;

This default-initializes foo, and since Foo's default constructor is trivial, it effectively doesn't initialize it at all, so foo._bar can hold any value (including 0).

Foo()

This value-initializes the temporary object, which in case of trivial default constructor means zero-initialization, so Foo()._bar is equal to 0.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Sorry if this is a little pedantic, but you would definitely not want zero-initialization for a class like `struct String {std::string str;};` just because it doesn't have a user-provided default constructor. – chris Dec 15 '14 at 13:15
2

n3376 quotes

8.5/11

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. — end note ]

8.5/6

To default-initialize an object of type T means: if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

8.5/10

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

8.5/7

To value-initialize an object of type T means:

...

otherwise, the object is zero-initialized.

8.5/5

To zero-initialize an object or reference of type T means: if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;

So, in your case, there are nor static storage duration variables, nor thread-local variables, so objects foo and b will be default-initialized, that means, that constructor will be called. Default-constructor (not user-defined) will not initialize members and in members will be arbitrary garbage and this arbitrary garbage may be 0 (thanks to Jarod42 for point this in comment). And Foo().printBar(); should print 0, since object is zero-initialized.

Community
  • 1
  • 1
ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • 1
    And arbitrary garbage may be `0`. – Jarod42 Dec 15 '14 at 12:56
  • 1
    It's worth mentioning that `Foo().printBar();` must print 0 (on its own at least, not when there's other UB in the program). – chris Dec 15 '14 at 13:00
  • @Jarod42 That's actually a really good comment. I hadn't considered that default initialization might produce a 0 result. Hmmm... there must be a way to handle this so I can actually see what's happening. – Jonathan Mee Dec 15 '14 at 13:03