473,320 Members | 1,848 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Validity of pointer conversions

Are the following codes guaranteed to work always?
1.

#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;

for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};

some_func(array[0], sizeof(array)/sizeof(**array));

std::cout<< std::endl;
}
The above prints 50 zeros. I think it is guaranteed to work, since all
arrays are sequences of their elements.

2.

#include <iostream>
int main()
{
using namespace std;

int array[50]= {0};

int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);

for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";

cout<< endl;

}
Here p behaves as a 2-dimensional matrix, that is a 10x5 matrix. I think
it is guaranteed to work for the same reason as the first one, that is
we can treat an array (sequence) of integers as various types of integer
arrays.
Jan 5 '08 #1
25 1965
On Jan 5, 10:02 am, Ioannis Vranos <j...@no.spamwrote:
Are the following codes guaranteed to work always?

1.

#include <iostream>

inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;

for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";

}

int main()
{
int array[10][5]= {0};

some_func(array[0], sizeof(array)/sizeof(**array));

std::cout<< std::endl;

}

The above prints 50 zeros. I think it is guaranteed to work, since all
arrays are sequences of their elements.
// Are you sure? try...
int array[10][5]= {99};

// and as far as a function for an array:
template< typename T,
const std::size_t Rows,
const std::size_t Columns >
void some_func(T(& arr)[Rows][Columns])
{
// do stuff
}

// this works. guarenteed
std::vector< std::vector< int vvn(10, std::vector<int>(5, 99));
>
2.

#include <iostream>

int main()
{
using namespace std;

int array[50]= {0};

int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);

for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";

cout<< endl;

}

Here p behaves as a 2-dimensional matrix, that is a 10x5 matrix. I think
it is guaranteed to work for the same reason as the first one, that is
we can treat an array (sequence) of integers as various types of integer
arrays.
Anything written in C++ that requires a reinterpret_cast sounds an
alarm here. You can only guess at what the result might be (and
possible test/check the result with typeid).
Hacking is not programming. Respect your types at all costs. Its
directive #1, no exceptions.
Anytime you fool a compiler you are preventing it to help you code.
Basicly, you'll code as if it is a 10x5 matrix and then one day
something will change thats beyond your control.
You'll come back 6 months from now, look at your code, needing to
modify it (ie: add features) and reach for the Asprin tablets (imagine
the client-user of your code trying to figure it all out). An apple is
an apple, if you threat it like an orange then you'll eventually fall
in a hole called undefined behaviour. You will, its a question of
time.
Clients/Customers don't like hacks, and sometimes - that client/
customer ... is you.

Jan 5 '08 #2
On Jan 5, 4:02 pm, Ioannis Vranos <j...@no.spamwrote:
Are the following codes guaranteed to work always?
1.
#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
The above prints 50 zeros. I think it is guaranteed to work,
since all arrays are sequences of their elements.
And? I don't see any relationship between what you just said
and any guarantee of working. You have an array bounds
violation, which is undeefined behavior. And there have been
(and maybe still are) implementations which detect it, and
treat it as an error condition.
2.
#include <iostream>
int main()
{
using namespace std;
int array[50]= {0};
int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);
for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";
cout<< endl;
}
Here p behaves as a 2-dimensional matrix, that is a 10x5
matrix.
Almost nothing involving reinterpret_cast is guaranteed to work.
About the only thing that the standard guarantees is that if you
cast the value back to its original type, and you haven't
violated any alignment restrictions in the intermediate types,
the value will compare equal to the original value (and thus,
designate the same object designated by the original pointer).

From a quality of implementation point of view: the standard
does say that the conversion is expected to be "unsurprising"
for someone familiar with the addressing architecture of the
processor, so I would expect this to work on most
implementations.
I think it is guaranteed to work for the same reason
as the first one,
It is totally unrelated to the first. reinterpret_cast is quite
different from other conversions.
that is we can treat an array (sequence) of integers as
various types of integer arrays.
If by that you mean that you can play games with the dimensions,
as long as the total number of elements is unchanged, that is
simply false.

--
James Kanze (GABI Software) email:ja*********@gmail.com
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

Jan 5 '08 #3
On Jan 5, 6:35 pm, Salt_Peter <pj_h...@yahoo.comwrote:
On Jan 5, 10:02 am, Ioannis Vranos <j...@no.spamwrote:
Are the following codes guaranteed to work always?
1.
#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
The above prints 50 zeros. I think it is guaranteed to work,
since all arrays are sequences of their elements.
// Are you sure? try...
int array[10][5]= {99};
What does that change? You have different initial values
(array[0][0] == 99, all other elements == 0). But there is
still an array bounds violation in the function, which is
undefined behavior.
// and as far as a function for an array:
template< typename T,
const std::size_t Rows,
const std::size_t Columns >
void some_func(T(& arr)[Rows][Columns])
{
// do stuff
}
// this works. guarenteed
std::vector< std::vector< int vvn(10, std::vector<int>(5, 99));
That does something different. It initializes all of the
elements with 99, rather than the first with 99, and all of the
others with 0.
2.
#include <iostream>
int main()
{
using namespace std;
int array[50]= {0};
int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);
for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";
cout<< endl;
}
Here p behaves as a 2-dimensional matrix, that is a 10x5
matrix. I think it is guaranteed to work for the same reason
as the first one, that is we can treat an array (sequence)
of integers as various types of integer arrays.
Anything written in C++ that requires a reinterpret_cast
sounds an alarm here. You can only guess at what the result
might be (and possible test/check the result with typeid).
That's not quite true---there are a few things you can do with
reinterpret_cast which have defined behavior. But this isn't
one of them. On the other hand, the expressed intent of
reinterpret_cast in the standard is to support type punning, in
so far as reasonable on the underlying architecture, so from a
quality of implementation point of view, I would expect it to
work on most architectures.
Hacking is not programming. Respect your types at all costs.
Its directive #1, no exceptions.
C++ has reinterpret_cast for a reason. I use it, for example,
when implementing things like malloc or garbage collection. In
such cases, it's a necessary evil.

In anything but such low level (architecture dependent)
programming, of course, it's a guaranteed problem, if only for
reasons of readability.

(For the rest, I very much agree with the part I've cut.
Anything involving reinterpret_cast is a hack, and hacks should
be reserved for the cases where they are absolutely necessary.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 5 '08 #4
Salt_Peter wrote:
On Jan 5, 10:02 am, Ioannis Vranos <j...@no.spamwrote:
>Are the following codes guaranteed to work always?

1.

#include <iostream>

inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;

for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";

}

int main()
{
int array[10][5]= {0};

some_func(array[0], sizeof(array)/sizeof(**array));

std::cout<< std::endl;

}

The above prints 50 zeros. I think it is guaranteed to work, since all
arrays are sequences of their elements.

// Are you sure? try...
int array[10][5]= {99};

OK, it prints:

[john@localhost src]$ ./foobar-cpp
99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
as expected. When initialising a built-in array with initial values, the
rest members of the array that are not explicitly assigned with an
initial value, are initialised with 0.

>
// and as far as a function for an array:
template< typename T,
const std::size_t Rows,
const std::size_t Columns >
void some_func(T(& arr)[Rows][Columns])
{
// do stuff
}

// this works. guarenteed
std::vector< std::vector< int vvn(10, std::vector<int>(5, 99));

I am not looking for ways to do it. I am just asking if these specific
uses are guaranteed to work as expected.
Anything written in C++ that requires a reinterpret_cast sounds an
alarm here. You can only guess at what the result might be (and
possible test/check the result with typeid).
Hacking is not programming. Respect your types at all costs. Its
directive #1, no exceptions.
Anytime you fool a compiler you are preventing it to help you code.
Basicly, you'll code as if it is a 10x5 matrix and then one day
something will change thats beyond your control.
You'll come back 6 months from now, look at your code, needing to
modify it (ie: add features) and reach for the Asprin tablets (imagine
the client-user of your code trying to figure it all out). An apple is
an apple, if you threat it like an orange then you'll eventually fall
in a hole called undefined behaviour. You will, its a question of
time.
Clients/Customers don't like hacks, and sometimes - that client/
customer ... is you.

I am asking if it is a *valid* low-level behaviour, and not an undefined
behaviour. We could use

int (*p)[5]= static_cast<int (*)[5](static_cast<void *>(&array[0]));

instead of the reinterpret_cast instead.
My question (actually what I think I know and I want others to verify)
is, a built in array of type T, is a sequence of its members of type T,
and thus we can treat it as arrays of various forms.
Consider another example:
#include <iostream>
#include <cstdlib>
int main()
{
using namespace std;

const char *pc= "This is a test.";
// 100% guaranteed to work
const char *p1= pc;

while(*p1)
cout<< *p1++;

cout<< endl;

// 100% guaranteed to work
const char (*p2)[16]= reinterpret_cast<const char (*)[16]>(pc);

for(size_t j= 0; j<sizeof(*p2)/sizeof(**p2); ++j)
cout<< p2[0][j];

cout<< endl;
// ==Here is my question. Is it 100% guaranteed to work? AFAIK yes.
const char (*p3)[8]= reinterpret_cast<const char (*)[8]>(pc);

for(size_t i= 0; i<2; ++i)
for(size_t j= 0; j<sizeof(*p3)/sizeof(**p3); ++j)
cout<< p3[i][j];

cout<< endl;
}
Jan 5 '08 #5
James Kanze wrote:
On Jan 5, 4:02 pm, Ioannis Vranos <j...@no.spamwrote:
>Are the following codes guaranteed to work always?
>1.
>#include <iostream>
>inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
>int main()
{
int array[10][5]= {0};
> some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
>The above prints 50 zeros. I think it is guaranteed to work,
since all arrays are sequences of their elements.

And? I don't see any relationship between what you just said
and any guarantee of working. You have an array bounds
violation, which is undeefined behavior. And there have been
(and maybe still are) implementations which detect it, and
treat it as an error condition.

What exact array bounds violation is there in the code above?

"int array[10][5];" is a sequence of 50 integers.
>2.
>#include <iostream>
>int main()
{
using namespace std;
> int array[50]= {0};
> int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);
> for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";
cout<< endl;
}
>Here p behaves as a 2-dimensional matrix, that is a 10x5
matrix.

Almost nothing involving reinterpret_cast is guaranteed to work.

OK, consider int (*p)[5]= static_cast<int (*)[5](static_cast<void
*>(&array[0])); instead.

If by that you mean that you can play games with the dimensions,
as long as the total number of elements is unchanged, that is
simply false.

Why? In all cases we have the same sequence of ints, that is

int array[50], int array[10][5], int array[5][10] are all implemented as
the same sequence of 50 ints. If they are not implemented in the same
way, where do they differ?
Thanks.
Jan 5 '08 #6
Ioannis Vranos wrote:
James Kanze wrote:
>On Jan 5, 4:02 pm, Ioannis Vranos <j...@no.spamwrote:
>>Are the following codes guaranteed to work always?
>>1.
>>#include <iostream>
>>inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
>>int main()
{
int array[10][5]= {0};
>> some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
>>The above prints 50 zeros. I think it is guaranteed to work,
since all arrays are sequences of their elements.

And? I don't see any relationship between what you just said
and any guarantee of working. You have an array bounds
violation, which is undeefined behavior. And there have been
(and maybe still are) implementations which detect it, and
treat it as an error condition.


What exact array bounds violation is there in the code above?

"int array[10][5];" is a sequence of 50 integers.
No. It is a array of 10 arrays of 5 int. It so happens to be guaranteed that
the total of 50 int are arranged contiguously in memory, but that does not
magically turn int array[10][5] into an array of 50 int. Consequently, an
expression like

array[7][6]

is an out-of-bounds access to the 6th element (which does not exist) of the
7th array of 5 int.

>
>>2.
>>#include <iostream>
>>int main()
{
using namespace std;
>> int array[50]= {0};
>> int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);
>> for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";
cout<< endl;
}
>>Here p behaves as a 2-dimensional matrix, that is a 10x5
matrix.

Almost nothing involving reinterpret_cast is guaranteed to work.


OK, consider int (*p)[5]= static_cast<int (*)[5](static_cast<void
*>(&array[0])); instead.

>If by that you mean that you can play games with the dimensions,
as long as the total number of elements is unchanged, that is
simply false.


Why? In all cases we have the same sequence of ints, that is

int array[50], int array[10][5], int array[5][10] are all implemented as
the same sequence of 50 ints. If they are not implemented in the same
way, where do they differ?
They differ in type. This information is known to the compiler and the
compiler is free to detect that

array[7][6]

is an out-of-bounds access. You will not find that casting pointer makes any
guarantees that type-derived bounds can be moved via casting.

Best

Kai-Uwe Bux
Jan 5 '08 #7
jk********@gmx.net wrote:
>
>>>1.
#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
>What exact array bounds violation is there in the code above?

"int array[10][5];" is a sequence of 50 integers.

No. It is a array of 10 arrays of 5 int. It so happens to be guaranteed that
the total of 50 int are arranged contiguously in memory, but that does not
magically turn int array[10][5] into an array of 50 int. Consequently, an
expression like

array[7][6]

is an out-of-bounds access to the 6th element (which does not exist) of the
7th array of 5 int.


Yes I can understand that having defined an array with the name array as:

int array[10][5];

with array[7][6] we are accessing out of the array (and the
implementation sequence).
However in the above code I am using an int pointer to output all
members of the array (50 in total) in a 1-dimension array fashion. I do
not point to any element after the one past the end, or to any before
the first one. So I am not violating the boundaries of the sequence.
>int array[50], int array[10][5], int array[5][10] are all implemented as
the same sequence of 50 ints. If they are not implemented in the same
way, where do they differ?

They differ in type.

Yes I know they differ in type. Also it is 100% guaranteed to consider
any of the array examples above as an array of unsigned char.


This information is known to the compiler and the
compiler is free to detect that

array[7][6]

is an out-of-bounds access. You will not find that casting pointer makes any
guarantees that type-derived bounds can be moved via casting.


I do not know to which "array" definition you are referring with that.
Can you provide the definition along with your out-of-bounds access
example, and mention where I access out-of-bounds?
Thanks.
Jan 6 '08 #8
Ioannis Vranos wrote:
jk********@gmx.net wrote:
>>
>>>>1.
#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
>>What exact array bounds violation is there in the code above?

"int array[10][5];" is a sequence of 50 integers.

No. It is a array of 10 arrays of 5 int. It so happens to be guaranteed
that the total of 50 int are arranged contiguously in memory, but that
does not magically turn int array[10][5] into an array of 50 int.
Consequently, an expression like

array[7][6]

is an out-of-bounds access to the 6th element (which does not exist) of
the 7th array of 5 int.

Yes I can understand that having defined an array with the name array as:

int array[10][5];

with array[7][6] we are accessing out of the array (and the
implementation sequence).
Out of the 7th array, yes; but not out of the implementation sequence:

7*5+6 = 41 < 10*5 = 50

However in the above code I am using an int pointer to output all
members of the array (50 in total) in a 1-dimension array fashion. I do
not point to any element after the one past the end, or to any before
the first one. So I am not violating the boundaries of the sequence.
You _assume_ that 50 int that lie contiguously in memory can be treated as
an array and that pointer-arithmetic can be used to access any of them
given a pointer to the first. That hypothesis, however, is not waranted by
the standard. To get rid of all the function call ramifications in your
example, consider the following:

int array [10][5] = {0};
int* p = &array[0][0]; // line 2
std::cout << p[5] << '\n'; // line 3

This compares to your code since &array[0][0] is what array[0] decays to
when passed as a parameter to some_func.

The question is whether the third line has undefined behavior. Consider the
following hypothetical implementation of pointers: a pointer is a tripple
of three addresses, the first to the pointee and the other two specifying a
valid range for pointer arithmetic. Similarly, every array (static or
dynamic) has its bounds stored somewhere. When an array decays to a
pointer, these bounds are used to deduce the range for the pointer.
Whenever pointer-arithmetic yields a pointer outside the valid range,
dereferencing triggers a segfault. Such an implementation is not ruled out
by any provisions of the standard that I know of.

Note that in line 2, the compiler has static type information about the rhs.
The rhs is a pointer to the first element in an array of 5. Thus, in the
described implementation, p will be given a range of size 5 and line 3 is
an out-of-bounds access since it dereferences the past-end position of the
5 int sequence array[0] in a way that is obtained through pointer
arithmetic from a pointer into the 5 int sequence array[0].

If you think that a range-checking implementation is not standard
conforming, please provide some language from the standard supporting your
point of view. Note that the guarantee of arrays being contiguous is met by
such an implementation. What such an implementation prevents is just the
reinterpretation of array sizes through pointer casting and pointer
arithmetic.
[snip]

Best

Kai-Uwe Bux
Jan 6 '08 #9
Salt_Peter wrote:
>
>>// Are you sure? try...
int array[10][5]= {99};
OK, it prints:

[john@localhost src]$ ./foobar-cpp
99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0

Uh, no it doesn't

99, 0, 0, 0, 0,
99, 0, 0, 0, 0,
-8 more times-

Can you provide the exact code you are using, along with its output?

Jan 6 '08 #10
On Jan 5, 10:02 pm, Ioannis Vranos <j...@no.spamwrote:
James Kanze wrote:
On Jan 5, 4:02 pm, Ioannis Vranos <j...@no.spamwrote:
Are the following codes guaranteed to work always?
1.
#include <iostream>
inline void some_func(int *p, const std::size_t SIZE)
{
using namespace std;
for(size_t i=0; i<SIZE; ++i)
cout<< p[i]<< " ";
}
int main()
{
int array[10][5]= {0};
some_func(array[0], sizeof(array)/sizeof(**array));
std::cout<< std::endl;
}
The above prints 50 zeros. I think it is guaranteed to work,
since all arrays are sequences of their elements.
And? I don't see any relationship between what you just said
and any guarantee of working. You have an array bounds
violation, which is undeefined behavior. And there have been
(and maybe still are) implementations which detect it, and
treat it as an error condition.
What exact array bounds violation is there in the code above?
"int array[10][5];" is a sequence of 50 integers.
No. "int array[10][5]" is an array[10] of array[5] of int. The
standard may require that it be physically laid out as a
sequence of 50 integers, but that has nothing to do with the
type. The type is "int [10][5]". When it decays to a
pointer, the type is "int (*)[5]", a pointer to the first of
ten elements, and when you dereference said pointer, the result
is an int*, pointer to the first of five elements.

The authors of the C standard went out of there way to ensure
that an implementation which tracked bounds would be legal.
2.
#include <iostream>
int main()
{
using namespace std;
int array[50]= {0};
int (*p)[5]= reinterpret_cast<int (*)[5](&array[0]);
for (size_t i= 0; i< 10; ++i)
for(size_t j=0; j<5; ++j)
cout<< p[i][j]<<" ";
cout<< endl;
}
Here p behaves as a 2-dimensional matrix, that is a 10x5
matrix.
Almost nothing involving reinterpret_cast is guaranteed to work.
OK, consider int (*p)[5]= static_cast<int (*)[5](static_cast<void
*>(&array[0])); instead.
If by that you mean that you can play games with the dimensions,
as long as the total number of elements is unchanged, that is
simply false.
Why?
The short answer is because the standard says so. The rationale
behind this is to allow bounds checking implementations. Such
implementations have existed, and may still exist. (Centerline
offered one, and I believe that the Centerline compiler is still
on the market.)
In all cases we have the same sequence of ints,
So? What does physical layout have to do with type?
that is int array[50], int array[10][5], int array[5][10] are
all implemented as the same sequence of 50 ints. If they are
not implemented in the same way, where do they differ?
The underlying physical layout may be the same, but that doesn't
mean that they have the same type.

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 6 '08 #11
James Kanze wrote:
>
>that is int array[50], int array[10][5], int array[5][10] are
all implemented as the same sequence of 50 ints. If they are
not implemented in the same way, where do they differ?

The underlying physical layout may be the same, but that doesn't
mean that they have the same type.

I didn't say that they are of the same type, I was just wondering if we
can "adjust" arrays to behave the way we want, one dimensional array,
two dimensional array and so for.

I got the idea from valarray & slice combinations to "create" matrices
as we like (2x4, 3x5 and so on) while the whole thing is really based on
a 1-dimensional valarray.

Thought perhaps we could do the same with built in arrays by using
pointers since as you said, the physical implementation is the same.

I think it would be great if this was guaranteed to work, the same way
there is a special guarantee that we can treat POD objects as sequences
of chars/unsigned chars.
Jan 6 '08 #12
On 2008-01-06 16:09, Ioannis Vranos wrote:
James Kanze wrote:
>>
>>that is int array[50], int array[10][5], int array[5][10] are
all implemented as the same sequence of 50 ints. If they are
not implemented in the same way, where do they differ?

The underlying physical layout may be the same, but that doesn't
mean that they have the same type.


I didn't say that they are of the same type, I was just wondering if we
can "adjust" arrays to behave the way we want, one dimensional array,
two dimensional array and so for.

I got the idea from valarray & slice combinations to "create" matrices
as we like (2x4, 3x5 and so on) while the whole thing is really based on
a 1-dimensional valarray.

Thought perhaps we could do the same with built in arrays by using
pointers since as you said, the physical implementation is the same.

I think it would be great if this was guaranteed to work, the same way
there is a special guarantee that we can treat POD objects as sequences
of chars/unsigned chars.
Why not just use a one-dimensional array of sufficient size and wrap it
in a class and use some simple calculations to get the correct element,
it is guaranteed to work and in the end it will probably generate the
same code. Using proxy classes you can even get the same syntax (but I
would prefer to simply overload operator() ).

--
Erik Wikström
Jan 6 '08 #13
On 5 Jan, 15:02, Ioannis Vranos <j...@no.spamwrote:
Are the following codes guaranteed to work always?
<snip>

Ioannis,

They aren't guaranteed to work, and I can think of at least one
architecture in which they probably won't.

On the 8086 memory model, an int is usually 16 bits, and a "segment"
of memory is 64Kb - allowing 32k ints. Segments are 16 bytes apart -
a "paragraph" - and can overlap. You can tell the compiler to work in
several ways to get around this limit.

If the compiler is working in such a mode as to give a different
segment address for each of your 10 arrays, each one will be padded up
to the next paragraph boundary, so you'll have 10 bytes for the 5
integers - and then 6 bytes free (almost the same as padding). The
next array will be on the next paragraph boundary. There's no bounds
checking, so the first 5 integers will be the ones you expect, the
next 3 will be the padding, and only then will you get the 2nd 5.

Ciao
Jan 6 '08 #14
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. 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).
C++ has reinterpret_cast for a reason. *I use it, for example,
when implementing things like malloc or garbage collection. *In
such cases, it's a necessary evil.

In anything but such low level (architecture dependent)
programming, of course, it's a guaranteed problem, if only for
reasons of readability.
One can make separated namespaces to locate code specific for each
architecture and later to write using for correct one. A general
(suitable for all platforms, but with bad performance) form of the
code often can exist also in own namespace.

Maksim A. Polyanin
old page about some C++ improvements:
http://grizlyk1.narod.ru/cpp_new
Jan 6 '08 #15
Grizlyk wrote:
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. 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.


Brian
Jan 6 '08 #16
Imagine having a vector<intv(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<intvi(100);

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

somefunc(p);
Jan 7 '08 #17
Default User wrote:
>
The clause "the same as objects that have static storage
duration" is not the same as "filled by zero".

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.
Let's look like compilers make static 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.
_b:
* *.byte * 1
* *.byte * 2
* *.space 98
or
_b label * byte
* *db * * *1
* *db * * *2
* *db * * *98 * * *dup(?)
is oputput compiled from C++ into assembler for a concrete CPU.
all depends from asm conventions for ".space" and
"dup(?)" directives.
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
Jan 7 '08 #18
On Jan 6, 9:22 pm, Grizlyk <grizl...@yandex.ruwrote:
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.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 8 '08 #19
On Jan 7, 8:52 pm, Grizlyk <grizl...@yandex.ruwrote:
Default User wrote:
The clause "the same as objects that have static storage
duration" is not the same as "filled by zero".
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.
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.

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 8 '08 #20
James Kanze wrote:
>
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.
Jan 8 '08 #21
James Kanze wrote:
>
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? *
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
Jan 8 '08 #22
James Kanze wrote:
>
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
Jan 8 '08 #23
On Jan 8, 4:22 pm, Grizlyk <grizl...@yandex.ruwrote:
James Kanze wrote:
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.
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.
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()".
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).

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 9 '08 #24
James Kanze wrote:
>
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.)
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
Jan 9 '08 #25
On Jan 9, 2:26 pm, Grizlyk <grizl...@yandex.ruwrote:

[...]
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.

--
James Kanze (GABI Software) email:ja*********@gmail.com
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
Jan 9 '08 #26

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

22
by: Alex Fraser | last post by:
From searching Google Groups, I understand that void pointer arithmetic is a constraint violation, which is understandable. However, generic functions like qsort() and bsearch() must in essence do...
16
by: jacob navia | last post by:
Valid pointers have two states. Either empty (NULL), or filled with an address that must be at a valid address. Valid addresses are: 1) The current global context. The first byte of the data...
204
by: Alexei A. Frounze | last post by:
Hi all, I have a question regarding the gcc behavior (gcc version 3.3.4). On the following test program it emits a warning: #include <stdio.h> int aInt2 = {0,1,2,4,9,16}; int aInt3 =...
10
by: vb | last post by:
Hi all, I am a newbie in C and i want to know what all pointer conversions are "legal" according to ANSI C standard. For Example, int* to char*, some_struct* to char* and so on .. According to...
12
by: Vladimir_petter | last post by:
Dear All, There is the problem in nutshells (see the program bellow): It is ok to convert pointer to F<T> to the pointer to I. Now if I have pointer to member "F<T> entity::*" can I convert it...
8
by: Martin Jørgensen | last post by:
Hi, "C primer plus" p.382: Suppose we have this declaration: int (*pa); int ar1; int ar2; int **p2;
33
by: a | last post by:
Hi, I have a pointer that points to an unknown heap memory block, is it possible to check the pointer + 3 is valid or not? If it is impossible, how can I do the check? Thanks
5
by: jason.cipriani | last post by:
There have been some recent threads about casting pointers to and from void* that have me rethinking some of my usual practices. I have a couple of questions. 1. What is the purpose of C++'s...
8
by: tfelb | last post by:
Hey group! I have 2 questions. I saw functions with char *dst = (char *)src. In that case if I remember what I've learned I assign (an) (the) address of src to dst. Right? But I can assign...
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.