Validity of pointer conversions

G

Grizlyk

Default said:
I think it is not a best idea to trust that all other elements will be
zero. Default constructor for int will be called, and i have some
compilers, which do nothing in the case (a trash from previous memory
users will be found in the array). The same thing does for pointers
(int* for example).

That's not correct. If an incomplete initializer is used, all other
elements will be initialized as if it were set to 0. I don't know if
the C++ standard covers it specifically (my copy is at work), as it's
inherited from C. Here's the C99 draft standard on the issue:

       [#21] If there are fewer initializers  in  a  brace-enclosed
       list  than there are elements or members of an aggregate, or
       fewer characters in a string literal used to  initialize  an
       array  of  known  size than there are elements in the array,
       the  remainder  of  the  aggregate  shall   be   initialized
       implicitly  the  same  as  objects  that have static storage
       duration.

Yes, i have been confused with arrays without partitional
initialization, that is not the same.
 
I

Ioannis Vranos

Imagine having a vector<int> v(100); and want to pass it to a function
expecting a built-in array of int[2][50]

If it was guaranteed to work, we could just do:


vector<int> vi(100);

int (*p)[50]= reinterpret_cast<int(*)[50]> (&vi[0]);

somefunc(p);
 
D

Default User

Yes, i have been confused with arrays without partitional
initialization, that is not the same.


Right. Automatic aggregates without an initializer have indeterminant
values.



Brian
 
G

Grizlyk

Default said:
Right. Automatic aggregates without an initializer
have indeterminant values.

By the way, "the remainder of the aggregate shall be initialized
implicitly the same as objects that have static storage duration."

The clause "the same as objects that have static storage duration" is
not the same as "filled by zero". Let's look like compilers make
static arrays:

char b[100]={1,2};

_b:
.byte 1
.byte 2
.space 98
or

_b label byte
db 1
db 2
db 98 dup(?)

or

_b label byte
db 1
db 2
db 98 dup(0)

in first two cases all depends from asm conventions for ".space" and
"dup(?)" directives. It is possible, that third-part asm will treat
the directives not only as zero-filling.

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
 
D

Default User

Grizlyk said:
By the way, "the remainder of the aggregate shall be initialized
implicitly the same as objects that have static storage duration."

The clause "the same as objects that have static storage duration" is
not the same as "filled by zero".

From the C++ standard:

3.6.2 Initialization of non-local objects [basic.start.init]
1 Objects with static storage duration (3.7.1) shall be
zero-initialized (8.5) before any other initialization takes place.
Zero-initialization and initialization with a constant expression are
collectively called static initialization; all other initialization is
dynamic initialization. Objects of POD types (3.9) with static storage
duration initialized with constant expressions (5.19) shall be
initialized before any dynamic initialization takes place. Objects with
static storage duration defined in namespace scope in the same
translation unit and dynamically initialized shall be initialized in
the order in which their definition appears in the translation unit.
[Note: 8.5.1 describes the order in which aggregate members are
initialized. The initialization of local static objects is described in
6.7. ]



The particular example was for ints. So the zero-initialization is the
only one that will take place.

Let's look like compilers make
static arrays:

char b[100]={1,2};

_b:
.byte 1
.byte 2
.space 98
or

_b label byte
db 1
db 2
db 98 dup(?)

or

_b label byte
db 1
db 2
db 98 dup(0)

in first two cases all depends from asm conventions for ".space" and
"dup(?)" directives. It is possible, that third-part asm will treat
the directives not only as zero-filling.

I have no idea what you are going on about. The standard requires
zero-initialization. If you think you see somethine else then either:

1. You're wrong.

2. You have a non-conforming implementation.


Now that I'm at work, I have access to the C++ standard. Here's what it
says:

8.5.1 Aggregates
If there are fewer initializers in the list than there are members in
the aggregate, then each member not explicitly initialized shall be
value-initialized (8.5). [Example:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of
an expression of the form int(), that is, 0. ]



In the example from the standard, the aggregate is a struct, but same
principle applies to other types, like arrays. Generally, all the
elements in not explicitly initalized will be set to the appropriate
"0" for the type. In the case we had, it was ints, so 0.




Brian
 
G

Grizlyk

Default said:
From the C++ standard:

1 Objects with static storage duration (3.7.1) shall be
zero-initialized (8.5) before any other initialization takes place.

This is quite clear now:

1. Default constructor for static POD type makes zero-initialized
value.

2. Default constructor for not static POD type can do nothing.

There are fewer initializers of an aggregate or an array of known
size, remainder of the aggregate or the array shall be initialized
implicitly the same as objects that have static storage duration.

3. So default constructor for not static POD type for remainder of the
aggregate or the array makes zero-initialized value.

4. For non-POD values default ctor will be called always, even for
automaic aggregates or arrays.
I have no idea what you are going on about.

It is easy to understand
char       b[100]={1,2};

is C++ source code with static array b.

is oputput compiled from C++ into assembler for a concrete CPU.

It is means, that if assembler can emit for directive "dup(?)"
executable with any random values excluding 0 (there are the
assemblers in the world), the C++ compiler became non-standard (will
have a non-conforming implementation).

I think, that C++ programmer must not take in account the low-level
details of C++ compiler implementation and trust, that considered
arrays will be zero initialized for "conforming implementation".

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
 
J

James Kanze

James Kanze wrote:
int array[10][5]= {99};
What does that change? You have different initial values
(array[0][0] == 99, all other elements == 0).
I think it is not a best idea to trust that all other elements
will be zero.

The standard says that all other elements will be zero. Are you
claiming that you've had problems with compiler errors in this
regard?
Default constructor for int will be called, and i have some
compilers, which do nothing in the case (a trash from previous
memory users will be found in the array). The same thing does
for pointers (int* for example).

Which compilers? The standard is very explicit: "if there are
fewer initializers in the list than there are members in the
aggregate, then each member not explicitly initialized shall be
default-initialized." (That's from C++98. The exact wording
has changed some, but in no way which affects PODs.)
 
J

James Kanze

Default User wrote:
This is quite clear now:
1. Default constructor for static POD type makes zero-initialized
value.
2. Default constructor for not static POD type can do nothing.

Only class types have constructors, and the default constructor
of a POD class does nothing. Ever.

Default initialization of a non-POD class type calls the default
constructor. Default initialization of an array
default-initializes each element. Default initialization of any
other type is zero initialization.

If a definition of an aggregate has an initializer list,
elements for which no initializer is given are default
initialized---for a POD type, that means zero initialized.

If a definition of POD has no initializer, then it is not
initialized. All objects (even those with user defined
constructors) with static lifetime are zero initialized before
program start-up, however.
 
I

Ioannis Vranos

James said:
All objects (even those with user defined
constructors) with static lifetime are zero initialized before
program start-up, however.


I assune you mean they are initialised in the style

T obj= T();

for these objects, since those with user defined constructors may be not
possible to initialise with 0.
 
G

Grizlyk

James said:
The standard says that all other elements will be zero.  Are you
claiming that you've had problems with compiler errors in this
regard?


Which compilers?  

Not shure about meaning of the word "claiming", but i had a
possibilities to get the memory garbage. I have posted in the thread
above description for the thrash appearance.

The error is outside of C++ scope - within interface between
particular C++ copmiler for concrete CPU and concrete native CPU
assembler. Is it interesting here?

Gcc v4.1 for x86 can create asm output with ".space". Free bcc32 v5
for x86 can create asm output with "dup(?)" (note, earlier versions of
the compiler emit "dup(0)" explicitly).

For ".space" it is required that 0 is by default for skipped values,
but for "dup(?)" is not.

But in both cases some assemblers can create for dup(?) and ".space"
any random values. They use "right" to do nothing for all that was not
explicitly defined, probably, in a hope to place all the undefined
values into BSS-like segments of executable :).

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
 
G

Grizlyk

James said:
Only class types have constructors, and the default constructor
of a POD class does nothing.  Ever.

The fact, that "default constructor of a POD class does nothing" can
be a point of confusion. Consider:

template<typename T>
void foo()
{
T a;
T b=T();

// ...
}

Is "T()" default constructor or not? "T()" is looks like default
constructor rather than initializer. But for

template<>
void foo<int>();

"b" will be initialized, but "a" will not.

"T b=0" looks like initializer more than "T b=T()".

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
 
J

James Kanze

James Kanze wrote:
I assune you mean they are initialised in the style
T obj= T();
for these objects, since those with user defined constructors
may be not possible to initialise with 0.

No, since if T has a non-trivial constructor, it has dynamic
initialization. Formally, initialization of variables with
static lifetime takes place in three steps:

1. Zero initialization. This takes place before any other
initialization, and affects *all* objects, regardless of
type.

Zero initialization of an arithmetic type, and enum or a
pointer is the same as initializing it with 0, converted to
the corresponding type. (Note that it does *not*
necessarily mean all bits zero, although this is the case on
most frequent architectectures.)

Zero initialization of a reference means nothing.

Zero initialization of a class type or an array means zero
initialization of each of a its elements. This recurses
down until you get to something in one of the above cases.

2. Static initialization. This concerns arithmetic types,
pointers and enums initialized with a constant expression,
and class types with trivial constructors initialized with
the aggregate initialization syntax in which all of the
initializers are constant expressions. It also concerns
arrays all of whose elements can be statically initialized.

3. Dynamic initialization. All of the rest, this involves
execution of code (which in turn can mean order of
initialization issues).

In practice, since static initialization doesn't involve
execution of code, there's no way a program can tell whether
zero initialization took place or not. The first line of code
you wrote only gets executed after static initialization is
finished. On the other hand, it is quite possible to observe
the "double" initialization when dynamic initialization is
involved:

#include <iostream>

extern int f() ;
struct Toto
{
int a ;
Toto() ;
} ;
Toto t ;
int b = f() ;

int f()
{
return 42 ;
}

Toto::Toto()
: a( b )
{
}

int
main()
{
std::cout << t.a << std::endl ;
return 0 ;
}

This program is guaranteed to output 0.

More generally, I use this to ensure correct initialization of a
singleton during static initialization (and thus, normally,
before threading starts).

(Technically, I'm not sure, but I think an implementation is
allowed to scribble over the memory before calling a
constructor, but practically, none do, and in some very
exceptional cases, I've also exploited this to allow objects to
be used before being constructed.)
 
J

James Kanze

James Kanze wrote:
The fact, that "default constructor of a POD class does
nothing" can be a point of confusion.

Yes, until you realize that "initialization" can be more than
just calling the constructor.
Consider:
template<typename T>
void foo()
{
T a;
T b=T();
// ...

Is "T()" default constructor or not? "T()" is looks like
default constructor rather than initializer.

Good point. T() is a special case:). More generally, of
course, if T is the name of a type, T(...) is a special case: it
constructs an object, according to a certain set of rules. If
the object is of class type, those rules will result in the
constructor being called (at the end). The expression itself,
of course, will do more than just call the constructor (and is
valid for non-class types which do not have constructors).

(Formally, T() is an "explicit type conversion (functional
notation)". Although I can't quite figure out what type is
being converted in cases like T(1,2,3)---"int, int, int" is not
a type in C++. I prefer to think of it as explicit creation of
an object. The exact words you use really don't matter, though,
as long as you realize that it is *not* just calling a
constructor.)

I might add the exact wording concerning this case has changed;
the original standard didn't correctly express exactly what was
wanted.
template<>
void foo<int>();
"b" will be initialized, but "a" will not.
"T b=0" looks like initializer more than "T b=T()".

Both are copy initialization, with exactly the same semantics.
In one case, what you copy is 0, in the other T(). The key here
is the sentence "An object whose initializer is an empty set of
parentheses, i.e., (), shall be value-initialized." (From the
latest draft---the current version of the standard says
"default-initialized". And the distinction between default
initialization and value-initialization was introduced to cover
some corner cases which weren't handled as intended in the
current standard.)

In practice, of course, all of these rules are overly
complicated, and only really of interest if you're writing a
compiler. For most people, it's sufficient to remember that 1)
static objects are zero initialized before program start up, and
2) initializing with an empty set of parentheses has more or
less the same effect as causing the initialization to be the
same as if the variable were static. I can construct cases
where 2 isn't true, but I can't imagine them being relevant to
any real code (and certainly not to any well written code).
 
J

James Kanze

James Kanze wrote:
Not shure about meaning of the word "claiming", but i had a
possibilities to get the memory garbage.

Again: with which compiler? I've never seen a compiler which
got this wrong.
I have posted in the thread above description for the thrash
appearance.
The error is outside of C++ scope - within interface between
particular C++ copmiler for concrete CPU and concrete native CPU
assembler. Is it interesting here?

I'm not sure what you're point is. The fact that you can write
code in assembler which results in trash values has nothing to
do with C++. *If* the C++ compiler goes through an assembler
phase (very few do today), then it is the responsibility of the
compiler to generate assembler code which has the required
semantics.
Gcc v4.1 for x86 can create asm output with ".space".

Gcc 4.1 doesn't generate assembler; it generates object code
directly. *If* you specify the -S option, it will generate
assembler, but obviously, that assembler code "supposes" a
specific assembler, on a specific machine, which does whatever
gcc supposes it does correctly. (And of course, gcc doesn't
really document what assumptions it makes concerning the
assembler. If you generate assembler code, and then try to use
it, it's up to you to ensure that everything is correct and what
you want. As a quality of implementation issue, gcc tries to
get the assembler "right", but it's really irrelevant to
C++.)
Free bcc32 v5 for x86 can create asm output with "dup(?)"
(note, earlier versions of the compiler emit "dup(0)"
explicitly).
For ".space" it is required that 0 is by default for skipped values,
but for "dup(?)" is not.

Presumably, free bcc323 supposes a compiler (or an environment)
where dup(?) does result in 0 initialization. (Just a guess,
but I suspect that dup(0) will result in the data being laid out
in the data segment on disk, where as dup(?) will put the data
in the bss segment, with no initialization on disk---this wasn't
how the original Intel assembler worked, but it seems reasonable
for a Windows or Unix machine. Under Windows or Unix, the OS
initializes the bss to all bits 0, for security reasons, if
nothing else. And under Windows and Unix, it just happens that
zero initialization always corresponds to all bits 0.)
But in both cases some assemblers can create for dup(?) and
".space" any random values. They use "right" to do nothing for
all that was not explicitly defined, probably, in a hope to
place all the undefined values into BSS-like segments of
executable :).

And the OS's I use guarantee that all BSS-like segments are zero
initialized. (Any multi-user OS must guarantee that all memory
obtained from the system is initialized somehow---both Windows
and Unix guarantee 0.)
 
G

Grizlyk

James said:
And the OS's I use guarantee that all BSS-like segments are zero
initialized.  (Any multi-user OS must guarantee that all memory
obtained from the system is initialized somehow---both Windows
and Unix guarantee 0.)

I agree, that all (hidden for C++ standard) parts of C++ compiler must
work correctly and interact together perfectly. By standard the
considered C++ data must be initialized by zero.

But as you touch the scope outside of C++ standard, i can say, that C+
+ compiler can be used as front-end compiler, to produce intermediate
sources, and the sources can be used later in free manner to make
(with the help of particular codegenerators) concrete executable for
concrete environment.

It is very interesting for me, i can not prove my opinion, but never
heard befor, that any OS or executable environment take for itself the
job to do any certain things with the memory, that has been allocated
for process from OS. The stub-like program _can_ do any initialization
itself for the allocated memory, but there are no any garantees, that
any OS or stub _must_ do something, just because random filled memory
is initialized somehow also.

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
 
J

James Kanze

[...]
It is very interesting for me, i can not prove my opinion, but
never heard befor, that any OS or executable environment take
for itself the job to do any certain things with the memory,
that has been allocated for process from OS. The stub-like
program _can_ do any initialization itself for the allocated
memory, but there are no any garantees, that any OS or stub
_must_ do something, just because random filled memory is
initialized somehow also.

Well, an OS could fill the memory with random values, rather
than 0, I suppose. But just leaving whatever happened to be
there would be a serious security leak. (Suppose that the last
process to use that memory just happened to store your password
in it.)

Unix always initialized the "bss" segment to 0, from the day 1.
I don't think MS-DOS pre-initialized anything, but I'm pretty
sure Windows does.
 

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,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top