About initializing non-local object

W

WaterWalk

Hello. When I consult the ISO C++ standard, I notice that in
paragraph 3.6.2.1, the standard states:
"Objects with static storage duration shall be zero-initialized before
any other initialization takes place."

Does this mean all non-local objects will be zero-initialized before
they are initialized by their initializers(if they have)? For example:

int g_var = 3;
int main() {}

Will g_var be first initialized to zero, then to 3?
 
R

Rolf Magnus

WaterWalk said:
Hello. When I consult the ISO C++ standard, I notice that in
paragraph 3.6.2.1, the standard states:
"Objects with static storage duration shall be zero-initialized before
any other initialization takes place."

Is that a draft? Mine says:

"The storage for objects with static storage duration shall be
zero-initialized before any other initialization takes place."

This sounds more logical, since in C++, an object can by definition be
initialized only once.
Does this mean all non-local objects will be zero-initialized before
they are initialized by their initializers(if they have)?

I guess it does - more or less. See below.
For example:

int g_var = 3;
int main() {}

Will g_var be first initialized to zero, then to 3?

Well, you won't notice any difference, since it will be initialized to 3
before you can access it for the first time. In such a case, the "as-if"
rule can be used, that says that the program doesn't need to do exactly as
the standard says. It just has to have the same observable effect as a
program that would. Since there is no standard-compliant way to observe
that the object's storage is initialized to zero before the object's real
initialization, compilers are free to not do that.
This is more relevant for objects that get initialized dynamically (i.e.
classes with constructors).
 
G

Greg Herlihy

Hello. When I consult the ISO C++  standard, I notice that in
paragraph 3.6.2.1, the standard states:
"Objects with static storage duration shall be zero-initialized before
any other initialization takes place."

Does this mean all non-local objects will be zero-initialized before
they are initialized by their initializers(if they have)? For example:

int g_var = 3;
int main() {}

Will g_var be first initialized to zero, then to 3?

Yes. Or more accurately, if a program were to observe g_var's value
that is the initialization sequence of g_var's value - that that
program would see.

Greg
 
W

WaterWalk

Yes. Or more accurately, if a program were to observe g_var's value
that is the initialization sequence of g_var's value - that that
program would see.

Greg

Well, if so, then global objects which have dependencies across
translation units will not be a problem, at least, no uninitialized
objects will be used. Like:
//file1.cpp
int g_var1 = 3;

//file2.cpp
extern int g_var1;
int g_var2 = g_var1;

No matter what the initialization order is, when g_var2 is
initialized, g_var1 will be in an initialized state.
 
W

WaterWalk

Is that a draft? Mine says:

"The storage for objects with static storage duration shall be
zero-initialized before any other initialization takes place."

This sounds more logical, since in C++, an object can by definition be
initialized only once

Mine is the 2003 version. Maybe yours is the 1998 version.
 
J

john

Well, if so, then global objects which have dependencies across
translation units will not be a problem, at least, no uninitialized
objects will be used. Like:
//file1.cpp
int g_var1 = 3;

//file2.cpp
extern int g_var1;
int g_var2 = g_var1;

No matter what the initialization order is, when g_var2 is
initialized, g_var1 will be in an initialized state.

I don't think C++ garantees that g_var1 will be initialized before
g_var2. It might or it might not (just a feeling from my experience in c++).
 
J

James Kanze

Yes. Or more accurately, if a program were to observe g_var's
value that is the initialization sequence of g_var's value -
that that program would see.

In theory. If the initialization is static (as it is here),
then there is no way a program can see the zero initialization
(and I know of no implementation which actually does it). If
the initialization isn't static, of course, then it does matter.
E.g.

extern int f() ;
extern int g() ;

int i = f() ;
int j = g() ;


int f()
{
return j ;
}

int g()
{
return 3 ;
}

This is guaranteed to initialize i to 0, and j to 3.
 
J

James Kanze

Well, if so, then global objects which have dependencies
across translation units will not be a problem, at least, no
uninitialized objects will be used.

That's not really true. Zero initialization doesn't affect
references, for example (which can't be zero initialized), and
doesn't do the right thing for objects with non-trivial
initializers. (Calling a virtual function on an object whose
vptr is null is not going to do anything good.)
Like:
//file1.cpp
int g_var1 = 3;
//file2.cpp
extern int g_var1;
int g_var2 = g_var1;
No matter what the initialization order is, when g_var2 is
initialized, g_var1 will be in an initialized state.

In this case, the initialization order is defined. Static
initialization (g_var1) always takes place before dynamic
intialization (g_var2). To extend my previous example:

int f() ;
int g() ;

int i = f() + g() ;
int j = g() ;
int k = 3 ;

int f() { return j ; }
int g() { return k ; }

guarantees that all three variables will end up 3.
 
R

Rolf Magnus

WaterWalk said:
Mine is the 2003 version. Maybe yours is the 1998 version.

Yes, it is. But then I can just wonder why they changed that part of the
text.
 
W

WaterWalk

That's not really true. Zero initialization doesn't affect
references, for example (which can't be zero initialized), and
doesn't do the right thing for objects with non-trivial
initializers. (Calling a virtual function on an object whose
vptr is null is not going to do anything good.)

I never thought an object's vptr might be zero even after zero
initialization.
In this case, the initialization order is defined. Static
initialization (g_var1) always takes place before dynamic
intialization (g_var2). To extend my previous example:

int f() ;
int g() ;

int i = f() + g() ;
int j = g() ;
int k = 3 ;

int f() { return j ; }
int g() { return k ; }

guarantees that all three variables will end up 3.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Thanks for your explanation.
 
J

James Kanze

Yes, it is. But then I can just wonder why they changed that
part of the text.

Maybe because it doesn't say what was meant. What does
zero-initializing the storage for an object mean, for that
matter? Storage is raw memory, at best, an array of unsigned
char. And initializing the underlying storage with all 0 bits
is definitly NOT what was meant (and would be incompatible with
C). The standard defines clearly what zero-initialization of an
object means (and it's clear that the object isn't "initialized"
in the classical sense after it, nor does the object "exist" as
such after such initialization, at least not if it isn't a POD).
 
J

Jidilo

Like:
In this case, the initialization order is defined. Static
initialization (g_var1) always takes place before dynamic
intialization (g_var2). To extend my previous example:

OK but what if:

extern int x1;
extern int x2;
int x1 = x2;
int x2 = x1;


?
 
J

James Kanze

OK but what if:
extern int x1;
extern int x2;
int x1 = x2;
int x2 = x1;

That's all dynamic initialization, so first zero initialization
will set both to 0, then dynamic initialization will occur in
the declaration order (within any single file), so both will end
up set to 0.

If you replace the last line with:

int const x2 = 5 ;

however (and modify the extern declaration correspondingly),
both will end up initialized with 5---the static initialization
of x2 will occur before the dynamic initialization of x1.
 
R

Rolf Magnus

James said:
Yes, it is. But then I can just wonder why they changed that

Maybe because it doesn't say what was meant. What does
zero-initializing the storage for an object mean, for that
matter?

Well, I read it up in the 98 standard, and it doesn't mean what I expected
it to. It doesn't seem to be defined for the storage for non-PODs at all,
only for PODs. What the standard calls "zero initializing the storage for
an object", I would have called "zero initializing an object", separating
storage (raw memory, for which "zero initializing" means setting all bits
to 0), and objects (for which "zero initializing" means giving it the
type's representation of the zero value).
Storage is raw memory, at best, an array of unsigned
char. And initializing the underlying storage with all 0 bits
is definitly NOT what was meant

That's what I would have expected though. But if that wasn't meant, what
was?
(and would be incompatible with C).

In which way?
The standard defines clearly what zero-initialization of an
object means (and it's clear that the object isn't "initialized"
in the classical sense after it, nor does the object "exist" as
such after such initialization, at least not if it isn't a POD).

I just find it stange that the object is said to be initialzed twice.
 
J

James Kanze

Well, I read it up in the 98 standard, and it doesn't mean
what I expected it to. It doesn't seem to be defined for the
storage for non-PODs at all, only for PODs. What the standard
calls "zero initializing the storage for an object", I would
have called "zero initializing an object", separating storage
(raw memory, for which "zero initializing" means setting all
bits to 0), and objects (for which "zero initializing" means
giving it the type's representation of the zero value).

Apparently, the text in C++98 didn't satisfy the committee,
either, since it got reworked in C++03. The basic idea was,
however, for an object with dynamic initialization to be
initialized as if it had no explicit initializer in C. Thus,
pointers are null, int's 0, doubles 0.0, etc. It's somewhat
difficult to formulate, however, because C++ has other types of
objects as well.
That's what I would have expected though. But if that wasn't
meant, what was?

More or less:

in a composite object (class or array), each of the
component parts is zero initialized, recursively, until you
reach a non-composite object,

for unions, the first member is zero initialized,

for pointers (including pointers to members) and arithmetic
types, zero initialization is the equivalent of initializing
with a 0, converted to the appropriate type, and

for references and internal data, nothing (you can't
initialize a reference with 0, and you can't legally do
anything which would require internal data, like the vptr,
until dynamic initialization runs).
In which way?

Except for the bit about references, the rules above are those
of C. In particular, pointers are initialized with a null
pointer (not with all bits 0), and floating point with 0.0
(which formally may not have all bits 0, although I've never
heard of a case where it was otherwise). And in something like:

union { char* a; char b[10000] ; } ;

only the element a is initialized (as a null pointer, which
might mean that b[0] != 0).
I just find it strange that the object is said to be
initialzed twice.

It is. But it's a very special kind of initialization. And
since it definitly takes type information into account, I don't
know what else you'd call it.
 
J

Jidilo

That's all dynamic initialization, so first zero initialization
will set both to 0, then dynamic initialization will occur in
the declaration order (within any single file)

Still having question about this. I have try this:

class A {
public:
A(const A& a, const std::string& name) { cout << name << endl; }
};


file 1:
-------
extern A a2;
A a1(a2, "a1");


file 2:
-------
extern A a1;
A a2(a1, "a2");

And got no compilation error. In this case, what is the behaviour ? is
it well defined ?

Thanks.
 
W

WaterWalk

What would you expect it to be?

I thought the object is initialized "normally" with that all members
zero. Thus it can be used like a normal object(calling member
functions, etc). But according to James Kanze, after zero-
initialization, an object(its vptr for example) may not be set up
properly.
 
W

WaterWalk

Still having question about this. I have try this:

class A {
public:
A(const A& a, const std::string& name) { cout << name << endl; }

};

file 1:
-------
extern A a2;
A a1(a2, "a1");

file 2:
-------
extern A a1;
A a2(a1, "a2");

And got no compilation error. In this case, what is the behaviour ? is
it well defined ?

Thanks.

It seems this should be discussed in two aspects:
1) If a1 and a2 are initialized before main, then when their
constructors are called, both are already zero-initialized. Since the
above code doesn't use the contents of a1 and a2, I don't think there
is any problem.

2) If a1 and a2 are initialized after main, their initializations
shall occur before their first uses. And according to the standard,
such an initialization shall take place before the object's first use
of any function or object defined in the same translation unit, the
calling of A's constructor looks like a indefinite recursive loop:
before call a2's constructor, a1 shall be initialized first. But to
initialize a1, a2's constructor shall be first called. And so on.
 
J

Jidilo

It seems this should be discussed in two aspects:
1) If a1 and a2 are initialized before main, then when their
constructors are called, both are already zero-initialized. Since the
above code doesn't use the contents of a1 and a2, I don't think there
is any problem.

I just wanted to spot this. Put some use of the the 'a' parameter in the
ctor body if you want: the content of a1 and a2 will be used.
2) If a1 and a2 are initialized after main, their initializations
shall occur before their first uses. And according to the standard,
such an initialization shall take place before the object's first use
of any function or object defined in the same translation unit, the
calling of A's constructor looks like a indefinite recursive loop:
before call a2's constructor, a1 shall be initialized first. But to
initialize a1, a2's constructor shall be first called. And so on.

Well, I tend to be agreed with, the compiler should get into generating
a recursive initialization. a1 needs a2 so let's generate a2 ctor call
first; to do that i need to generate call for a1 ctor first, etc...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,209
Latest member
NelsonJax

Latest Threads

Top