Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Validity of pointer conversions

Reply
Thread Tools

Validity of pointer conversions

 
 
Ioannis Vranos
Guest
Posts: n/a
 
      01-05-2008
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.
 
Reply With Quote
 
 
 
 
Salt_Peter
Guest
Posts: n/a
 
      01-05-2008
On Jan 5, 10:02 am, Ioannis Vranos <(E-Mail Removed)> wrote:
> 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.

 
Reply With Quote
 
 
 
 
James Kanze
Guest
Posts: n/a
 
      01-05-2008
On Jan 5, 4:02 pm, Ioannis Vranos <(E-Mail Removed)> wrote:
> 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:(E-Mail Removed)
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

 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      01-05-2008
On Jan 5, 6:35 pm, Salt_Peter <(E-Mail Removed)> wrote:
> On Jan 5, 10:02 am, Ioannis Vranos <(E-Mail Removed)> wrote:
> > 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:(E-Mail Removed)
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
 
Reply With Quote
 
Ioannis Vranos
Guest
Posts: n/a
 
      01-05-2008
Salt_Peter wrote:
> On Jan 5, 10:02 am, Ioannis Vranos <(E-Mail Removed)> wrote:
>> 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;
}
 
Reply With Quote
 
Ioannis Vranos
Guest
Posts: n/a
 
      01-05-2008
James Kanze wrote:
> On Jan 5, 4:02 pm, Ioannis Vranos <(E-Mail Removed)> wrote:
>> 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.
 
Reply With Quote
 
jkherciueh@gmx.net
Guest
Posts: n/a
 
      01-05-2008
Ioannis Vranos wrote:

> James Kanze wrote:
>> On Jan 5, 4:02 pm, Ioannis Vranos <(E-Mail Removed)> wrote:
>>> 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
 
Reply With Quote
 
Ioannis Vranos
Guest
Posts: n/a
 
      01-06-2008
http://www.velocityreviews.com/forums/(E-Mail Removed) 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.
 
Reply With Quote
 
jkherciueh@gmx.net
Guest
Posts: n/a
 
      01-06-2008
Ioannis Vranos wrote:

> (E-Mail Removed) 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
 
Reply With Quote
 
Salt_Peter
Guest
Posts: n/a
 
      01-06-2008
On Jan 5, 3:47 pm, Ioannis Vranos <(E-Mail Removed)> wrote:
> Salt_Peter wrote:
> > On Jan 5, 10:02 am, Ioannis Vranos <(E-Mail Removed)> wrote:
> >> 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


Uh, no it doesn't

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


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


Indeed, except you have mistaken an array with a 'sequence' of arrays.
There is a critical distinction to be made.

>
>
>
> > // 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.


Here is a typical story about Mr Hacker and Dr Programmer.
Its fictitious although you'ld be surprised how often it happens.

Customer approaches Mr Hacker, he requires a type X to be streameable
to file, a socket and to and from various interfaces.

struct X
{
...
};

Type X is a mix of characters, integers, floats and a few other
members, nothing special. Mr Hacker proceeds to reinterpret_cast an
array of X elements into an array of some primitive type (char, Byte)
and streams the data (including padding!) and completes the job in
about 16 hours. Mr Hacker pridefully sends the project back at which
point the Client announces he's got to stream 2 more types as well. Mr
Hacker's jaw drops, client looks at code, gives Mr Hacker **** for
streaming the padding. Mr Hacker needs 3 days to get that job done.
Client now wants a couple more classes to be streameable (and he needs
them NOW). Client sees little benefit to continue the contract.

Client goes to see Dr Programmer. Dr Programmer completes the project
in about 16 hours too. Client notices that no padding is being
transferred, nice. Client needs 15 new classes to use the system,
however. Mr Programmer looks at one class - derives it from type X,
writes an insertion operator and an extraction operator for X,
modifies the interface as the compiler complains about the required
pure-virtual functions missing.
Client asks when will the project be ready then?
Mr Programmer says: "sir, you don't need to change a single statement
in our code to make it work with your new class, as long as you derive
from this abstract class".
Mr Programmer: "in fact, the code is set up in such a way that the
compilation errors will tell you whats missing, if anything." (thats
what happens when you treat an apple like an apple)
Client goes back to office, takes out another one of his 15 derived
types, looks at Mr Programmer's modifications to X's derivative ...
and smiles: "wow, i can extend this code without calling Mr
Programmer, and i can do it effortlessly because the code is crystal
clear".

In your opinion - who gets the next contract opportunity?

>
> 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;
>
> }


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
pointer to integer to pointer conversions lithiumcat@gmail.com C Programming 11 05-08-2008 12:52 PM
Pointer to pointer to const conversions James Aguilar C++ 3 06-22-2006 09:00 AM
Pointer conversions and Data types conversions vb@gmail.com C Programming 10 08-05-2005 09:51 AM
Validity of the this pointer before the object is constructed tuko C++ 4 09-17-2004 05:04 AM
validity of using subclasses in pointer-to-pointer types? Marcus Alanen C++ 1 09-07-2003 10:42 AM



Advertisments