I ran a test program:
#include <iostream>
inline void print(int a, int b)
{
std::cout << a << ", " << b << '\n';
}
template<typename T> inline void print() { T t; print(t.i, T().i); }
struct A { int i; };
struct B { int i; B() { } };
struct C { int i; C() : i() { } };
int main()
{
int i;
print(i, int());
A a; print(a.i, A().i); print<A>();
B b; print(b.i, B().i); print<B>();
C c; print(c.i, C().i); print<C>();
return 0;
}
which produced the following results with two compilers:
GCC 3.0 MS Visual C++ 6.0
-------------------------------------------------
-1073742744, 0 -858993460, 0
134536756, 0 -858993460, -858993460
0, 0 -858993460, -858993460
134536776, 134531067 -858993460, -858993460
134531067, 134536776 -858993460, -858993460
0, 0 -858993460, -858993460
0, 0 -858993460, -858993460
Of course, the test results don't guarantee anything, and it cannot
be argued from the nonzero value that it was not default initialized,
but I just wanted to get a rough idea what they did in practice.
It is only in the case of explicit "int()" that the two compilers
agree. Which is right?
And it's a mystery to me why the result of
A a; print(a.i, A().i);
differs with that of
print<A>();
since the latter is supposed to do exactly what the former does.
(Actually I found it by accident while trying to make the test code
more compact.) Is it a GCC bug? Or am I wrong somewhere?
--
KIM Seungbeom <musi...@bawi.org>
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
>I'd like to know exactly in which cases objects of fundamental types
>are default initialized to zero.
>
>I ran a test program:
>
> #include <iostream>
>
> inline void print(int a, int b)
> {
> std::cout << a << ", " << b << '\n';
> }
>
> template<typename T> inline void print() { T t; print(t.i, T().i); }
the expression T() constructs a temporary object that is default
initialized - defect report 178 (part of TC1) changes this to value
initialized (c++ std sect 5.2.3)
The difference between default initialization and value initialization
affects non POD types that have no constructor - value initialization
gives POD members of these types zero initialization now - default
initialization leaves these POD members uninitialized
None of your structs are non POD without constructor so the defect
report doesn't affect your code
T() for struct A should zero initialize POD member i
T() for struct B,C calls the default constructor - for B this leaves
member i uninitialized - for C, the i() in the initializer list
(refer 12.6.2 para 3) produces default initialization [value
initialization in TC1] - for POD member i of C this means zero
initialization.
Your code is not a reliable way to check if something has been zeroed
because zeroes can be left on the stack or in registers so I suspect
this accounts for some of your results
>
> struct A { int i; };
> struct B { int i; B() { } };
> struct C { int i; C() : i() { } };
>
> int main()
> {
> int i;
> print(i, int());
// should output "random" , zero
> A a; print(a.i, A().i); print<A>();
// should output random, zero, random, zero
> B b; print(b.i, B().i); print<B>();
// should output random, random, random, random
> C c; print(c.i, C().i); print<C>();
// should output zero, zero, zero, zero
VC6 doesn't do very well.
In my experience its a bad idea to rely on zero initialization - for
something that really matters its best to explicitly zero it
(including using memset for arays of POD) unless you are sure your
compiler does it correctly and you don't want to port your code to a
different compiler.
> which produced the following results with two compilers:
> GCC 3.0 MS Visual C++ 6.0
> -------------------------------------------------
> -1073742744, 0 -858993460, 0
> 134536756, 0 -858993460, -858993460
> 0, 0 -858993460, -858993460
> 134536776, 134531067 -858993460, -858993460
> 134531067, 134536776 -858993460, -858993460
> 0, 0 -858993460, -858993460
> 0, 0 -858993460, -858993460
>
>Of course, the test results don't guarantee anything, and it cannot
>be argued from the nonzero value that it was not default initialized,
>but I just wanted to get a rough idea what they did in practice.
>
>It is only in the case of explicit "int()" that the two compilers
>agree. Which is right?
Not VC6
>
>And it's a mystery to me why the result of
>
> A a; print(a.i, A().i);
>
>differs with that of
>
> print<A>();
>
>since the latter is supposed to do exactly what the former does.
>(Actually I found it by accident while trying to make the test code
>more compact.) Is it a GCC bug? Or am I wrong somewhere?
Zeroes can be left on the stack or in registers.
For a definition of default initialization, read section 8.5 in the
c++ standard or see defect report 178 - the definition of default
initialization did not change in the defect report.
A declaration of an object with no initializer e.g.
T a;
produces default initialization for non POD and no initialization for
POD.
Non POD members and bases of a class that are not explicitly listed in
the member initializer list of a constructor are default initialized
- POD members and bases not listed are uninitialized. The implicit
(compiler supplied) default constructor performs the same
initialization as a user written default constructor with an empty
member initializer list.
Note that the constructor for
std::vector<int> v(12);
initializes each element of the vector with T() - so should produce
zero for POD elements but isn't guaranteed to produce zero
initialization for element type being non POD struct without a user
constructor unless the compiler follows TC1.
You can also use the form
std::vector<int> v(12,0);
Graeme
When they are static or parts of static objects (including global).
When they are initialized using ().
When they are parts of an object of POD type and the object is initialized
using ().
In addition one can overload operator new so that it zeros the memory in
which case all dynamically allocated objects (whose memory is allocated with
the overloaded operator new) will be zero initialized :)
So in your example A is POD, so a.i may not be zero, but A().i should;
B is not POD and it doesn't initialize i, so neither b.i nor B().i have to
be zero;
C is not POD, but it initializes i using (), so both c.i and C().i should be
zero.
> And it's a mystery to me why the result of
>
> A a; print(a.i, A().i);
>
> differs with that of
>
> print<A>();
>
> since the latter is supposed to do exactly what the former does.
> (Actually I found it by accident while trying to make the test code
> more compact.) Is it a GCC bug? Or am I wrong somewhere?
The value of a.i is unspecified, so may be 0 as well. E.g. the compiler
might reuse the same spot on the stack that was used for the A() temporary
and was zero initialized. Try shuffling the calls around and see if the
difference persists.
HTH
Artem Livshits
Brainbench MVP for C++
http://www.brainbench.com
[...]
> It is only in the case of explicit "int()" that the two compilers
> agree. Which is right?
MSVC is in error, with the struct C (the last two lines). Other than
that, the values you see are acceptable.
In the cases other than explicit "int()" (or, in the member
initialization list, the expression "i()"), the compiler can initialize
automatic variables with whatever value it chooses (usually, the
variable just gets whatever value the memory happened to have before).
This will, of course, vary from compiler to compiler. You can even get
different results with one compiler, depending on the compiler settings.
The correct output should be:
???, 0
???, ???
???, ???
???, ???
???, ???
0, 0
0, 0
where "???" means "any integer value".
Oh, yeah - the exception I mentioned. If you initialize an array of with
fewer elements than specified, then the remaining elements get zero
initialized:
{ // some block scope
int array[10] = {9};
}
The standard requires array[1] through array[9] to be zero-initialized.
Now, all this only applies to *automatic* objects (i.e. local variables).
The standard requires objects with static storage duration ("static
objects"), to be zero initalized before any other initialization occurs.
So, the following program will not throw an exception:
struct A
{
int i;
};
A aGlobal;
int main()
{
if (aGlobal.i != 0)
throw "Bad initialization.";
}
Now, for dynamic objects. Given:
{ // some block scope
A * aPtr = new A;
C * cPtr = new C;
int * intPtr1 = new int;
int * intPtr2 = new int();
}
The A object will use the default constructor, which (as we established
earlier) does not initialize the 'i' variable. The value of aPtr->i will
be indeterminate.
The C object will also use the default ctor, which initializes (or,
_should_ initialize - as I said MSVC gets it wrong) the 'i' variable.
The value of cPtr->i should be 0.
The situation with dynamic variables is a little more subtle for
fundamental types. The first integer, i.e. *intPtr1, will be
uninitialized, therefore indeterminate. By adding the parentheses for
the second integer, *intPtr2, becomes zero-initialized.
Clear as mud? :=)
--
Jim
[snip]
> > template<typename T> inline void print() { T t; print(t.i,
T().i); }
> >
> > struct A { int i; };
> > struct B { int i; B() { } };
> > struct C { int i; C() : i() { } };
> >
> > int main()
> > {
> > int i;
> > print(i, int());
> > A a; print(a.i, A().i); print<A>();
> > B b; print(b.i, B().i); print<B>();
> > C c; print(c.i, C().i); print<C>();
> > return 0;
> > }
>
>The correct output should be:
>???, 0
>???, ???
>???, ???
>???, ???
>???, ???
>0, 0
>0, 0
>
>where "???" means "any integer value".
>
This is not correct. The second and third lines correspond to
A a; print(a.i, A().i); print<A>();
a.i will be ??? but A().i should be zero because, as specified in
5.2.3 para 2, the expression T() produces default initialization,
which means zero initialization for a POD. Similarly in the template
print<A> - t.i will be random but T().i should be zero.
Graeme