Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Default values or undefined values?

52 views
Skip to first unread message

Thiago Adams

unread,
Oct 11, 2018, 1:21:42 PM10/11/18
to
#include <stdio.h>

struct X {
int i;
};


int main()
{
X *p = new X();
X x = {};
printf("%d %d", p->i, x.i);
delete p;
}

This code is printing 0 0 in release mode. (VC++ 17)

Is it guarantee to initialize 'i' with '0'?

I tried to find the answer in but it is not clear.

https://en.cppreference.com/w/cpp/language/default_initialization


woodb...@gmail.com

unread,
Oct 11, 2018, 3:33:48 PM10/11/18
to
On Thursday, October 11, 2018 at 12:21:42 PM UTC-5, Thiago Adams wrote:
> #include <stdio.h>
>
> struct X {
> int i;
> };
>
>
> int main()
> {
> X *p = new X();

You could also try:

X *p2 = new X;


In that case you would get undefined values.


Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net

james...@alumni.caltech.edu

unread,
Oct 11, 2018, 4:21:42 PM10/11/18
to
On Thursday, October 11, 2018 at 1:21:42 PM UTC-4, Thiago Adams wrote:
> #include <stdio.h>
>
> struct X {
> int i;
> };
>
>
> int main()
> {
> X *p = new X();

"If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted
(9.4). An implicitly-declared default constructor is an inline public
member of its class." (10.3.4p4).
"The implicitly-defined default constructor performs the set of
initializations of the class that would be performed by a user-written
default constructor for that class with no ctor-initializer (10.9.2) and
an empty compound-statement." (10.3.4p7)

"In a non-delegating constructor, if a given potentially constructed
subobject is not designated by a meminitializer-id (including the case
where there is no mem-initializer-list because the constructor has no
ctor-initializer), then
[several options that don't apply]
— otherwise, the entity is default-initialized (9.3)." (10.9.2p9)

"To default-initialize an object of type T means:
(7.1) — If T is a (possibly cv-qualified) class type (Clause 10), ...
(7.2) — If T is an array type, ...
(7.3) — Otherwise, no initialization is performed." (9.3p7)

> X x = {};
> printf("%d %d", p->i, x.i);
> delete p;
> }
>
> This code is printing 0 0 in release mode. (VC++ 17)
>
> Is it guarantee to initialize 'i' with '0'?

No.

Thiago Adams

unread,
Oct 11, 2018, 4:58:36 PM10/11/18
to
Thanks, it helped to understand.

Öö Tiib

unread,
Oct 11, 2018, 5:03:40 PM10/11/18
to
The X is aggregate so these are guaranteed to default initialize
everything in *p and x:

X *p = new X();
X x = {};

And these leave *p and x uninitialized:

X *p = new X;
X x;

That means unless x is in static storage (there it is also
default initialized).

Jorgen Grahn

unread,
Oct 12, 2018, 4:27:00 AM10/12/18
to
I recently had to find out, and I can report that "The C++ Programming
Language" explains it well.

But if you have to know, I think you're either working with C structs,
or (my case) trying to understand broken code. It's almost always
better to let the class itself decide what should be initialized.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Chris Vine

unread,
Oct 12, 2018, 6:00:09 AM10/12/18
to
Default initialization does not tell the whole story here. C++ is so
convoluted on object initialization that you are better off initializing
data members via explicit constructors.

Having said that, as I understand it the expression 'X *p = new X()'
performs value initialization:
https://en.cppreference.com/w/cpp/language/value_initialization
This means that, where as here X is a non-union class type without any
user-provided constructors, each non-static data member, namely 'i' is
zero initialized. (The same would not be true of the expression 'X
*p = new X': that would perform default initialization and leave 'i'
uninitialized.)

As I understand it, the expression 'X x = {}' performs list
initialization and, because X is an aggregate, so aggregate
initialization:
https://en.cppreference.com/w/cpp/language/aggregate_initialization
This in turn means that because no explicit value is provided for the
data member 'i', it is value initialized and so zero initialized.

Chris Vine

unread,
Oct 12, 2018, 6:32:55 AM10/12/18
to
On Fri, 12 Oct 2018 10:59:48 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> Having said that, as I understand it the expression 'X *p = new X()'
> performs value initialization:
> https://en.cppreference.com/w/cpp/language/value_initialization
> This means that, where as here X is a non-union class type without any
> user-provided constructors, each non-static data member, namely 'i' is
> zero initialized. (The same would not be true of the expression 'X
> *p = new X': that would perform default initialization and leave 'i'
> uninitialized.)

By the way, in C++98 'new X()' and 'new X' both did the same thing, and
only default initialize, so leaving 'i' uninitialized. This was
changed in C++03 so that 'new X()' performs value initialization.

You would have to be using a very old compiler to fall foul of the old
behaviour.

Chris Vine

unread,
Oct 12, 2018, 7:02:43 AM10/12/18
to
Out of interest why do you say that zero initialization of 'i' does not
take place, via value initialization (for 'new X()') and aggregate
initialization (for 'X x = {}')?

james...@alumni.caltech.edu

unread,
Oct 12, 2018, 8:44:38 AM10/12/18
to
> take place, via value initialization (for 'new X()') ...

"If the new-initializer is omitted, the object is default-initialized." (5.3.4p17).

"To default-initialize an object of type T means:
(7.1) — If T is a (possibly cv-qualified) class type (Clause 9), constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one for the initializer () is chosen through overload resolution (13.3). The constructor thus selected is called, with an empty argument list, to
initialize the object." (9.3p7).

The citations I gave earlier indicate that a suitable constructor is implicitly declared and implicitly defined, and give the rules that apply when that constructor is called.

> ... and aggregate
> initialization (for 'X x = {}')?

I didn't think about the "X x = {};" initialization - that's my only excuse.

"If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its default member initializer (9.2) or, if there is no default member initializer, from an empty initializer list (8.5.4)." (8.5.1p7)

"Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized." (8.5.4p3)

"To value-initialize an object of type T means:
...
-- 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." (8.5p8)

So x.i == 0 is guaranteed.

Chris Vine

unread,
Oct 12, 2018, 9:22:29 AM10/12/18
to
On Fri, 12 Oct 2018 05:44:27 -0700 (PDT)
I don't think there is anything wrong with your earlier citations
(clearly X has an implicitly defined default constructor) but they are
incomplete. In particular, in the expression 'new X()' the initializer
is provided (in 'new X' it is omitted).

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

"To value-initialize an object of type T means ... if T is a
(possibly cv-qualified) class type without a user-provided or deleted
default constructor, then the object is zero-initialized ..."

"To zero-initialize an object ... of type T means ... if T is a
scalar type (6.9), the object is initialized to the value obtained by
converting the integer literal 0 (zero) to T ... if T is a (possibly
cv-qualified) non-union class type, each non-static data member ... is
zero-initialized"

Anyway, I take my lead from cppreference.com, which is of high quality
and which has already done the hard work. Reading the standard too much
can drive the reader mad. So I am comfortable that even if the
reasoning is wrong the answer (that 'i' is zero initialized) is right.

Chris Vine

unread,
Oct 12, 2018, 11:09:36 AM10/12/18
to
On 12 Oct 2018 14:31:31 GMT
r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> james...@alumni.caltech.edu writes:
> >"If the new-initializer is omitted, the object is default-initialized." (5.3.4p17).
>
> That would be
>
> new X
>
> , which generates
>
> movl $4, %ecx
> call _Znwy
>
> here. But
>
> new X()
>
> generates
>
> movl $4, %ecx
> call _Znwy
> movl $0, (%rax)
>
> . So the venerable GCC: (GNU) 9.0.0 20180909 (experimental)
> with -Ofast -O3 thinks that »i« has to be initialized to zero
> as soon as those parentheses (a new-initializer) are supplied.
>
> In the end, I feel too confused for the moment and might
> look into this again later (paragraph hunting in the standard).

It really isn't confusing and is set out in TC++PL and cppreference.com.
'new X()' (which has the initializer '()') value-initializes, and 'new
X' doesn't. If you really want to go paragraph hunting (what's wrong
with cppreference.cpp?), here are the references applying in C++14 for
'new X()', in logical order (the order your should read them) not
numerical order:

§5.3.4/17.2
§8.5/11
§8.5/8.2
§8.5/6.2
§8.5/6.1

Manfred

unread,
Oct 12, 2018, 12:10:47 PM10/12/18
to
The Standard is obviously the right place to look, as you did.
Out of curiosity, one way to experiment the different initializations is:

=====================================================
include <iostream>

struct X
{
int i;
};

void heap_init()
{
unsigned char a[16*sizeof(int)];

for(unsigned char *aa = a; a+sizeof(a) > aa; ++aa)
{
*aa = 0xFF;
}

X *p1 = new(a) X;
X *p2 = new(p1+1) X{};
X *p3 = new(p2+1) X();

std::cout << "p1->i: " << p1->i << std::endl;
std::cout << "p2->i: " << p2->i << std::endl;
std::cout << "p3->i: " << p3->i << std::endl;

p3->~X();
p2->~X();
p1->~X();

for(unsigned char *aa = a; a+sizeof(a) > aa; ++aa)
{
*aa = 0xFF;
}
}

void stack_init()
{
X x1;
X x2{};

std::cout << "x1.i: " << x1.i << std::endl;
std::cout << "x2.i: " << x2.i << std::endl;
}

int main()
{
heap_init();
stack_init();
}

=====================================================

$ c++ -O0 x.cc && ./a.out
p1->i: -1
p2->i: 0
p3->i: 0
x1.i: 32766
x2.i: 0

=====================================================

Juha Nieminen

unread,
Oct 13, 2018, 4:15:45 AM10/13/18
to
Jorgen Grahn <grahn...@snipabacken.se> wrote:
> But if you have to know, I think you're either working with C structs,
> or (my case) trying to understand broken code. It's almost always
> better to let the class itself decide what should be initialized.

In general, in the vast majority of cases, it's a good idea, and
good design, to always have all members initialized to known
default values.

However, there may be some fringe cases where utmost and absolute
efficiency is required and you really *don't* want to default-
initialize millions of objects with values that won't get used
anyway (most usually because they are immediately after assigned
new values, and the original default values aren't used for
anything.)
0 new messages