 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Old Wolf Guest
|
Posted: Sun Jul 11, 2004 10:34 am Post subject: casting int to int* |
|
|
On Herb Sutter's blog he writes:
| Quote: | Consider the following well-formed ISO C++ program
with well-defined semantics:
int* pi = new int(42); // line 1
pi = (int*)((int)pi ^ 0xaaaaaaaa);
// ... do other work ...
pi = (int*)((int)pi ^ 0xaaaaaaaa);
cout << *pi; // perfectly ok, prints "42", won't crash
delete pi; // ok
|
I thought this was implementation-defined behaviour, and I don't
see how it works in the case of 16bit int but 32bit int *
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
llewelly Guest
|
Posted: Sun Jul 11, 2004 11:52 pm Post subject: Re: casting int to int* |
|
|
[email]oldwolf (AT) inspire (DOT) net.nz[/email] (Old Wolf) writes:
| Quote: | On Herb Sutter's blog he writes:
Consider the following well-formed ISO C++ program
with well-defined semantics:
int* pi = new int(42); // line 1
pi = (int*)((int)pi ^ 0xaaaaaaaa);
// ... do other work ...
pi = (int*)((int)pi ^ 0xaaaaaaaa);
cout << *pi; // perfectly ok, prints "42", won't crash
delete pi; // ok
I thought this was implementation-defined behaviour, and I don't
see how it works in the case of 16bit int but 32bit int *
|
Same problem in the (perhaps more common) case of 32 bit int and 64
bit int* .
However, if your implementation defines sizeof(int) >= sizeof(int*),
there's a line in 5.2.10/5 which applies:
# ... A pointer converted to an integer of sufficient size ... and
# back to the same pointer type will have its original value. ...
But Herb doesn't merely store the pointer in an int for a time - he
does an exclusive or on it, and then puts the result back into a
pointer. So he can't escape through the loophole of 5.2.10/5 .
He's relying on the implementation-defined mapping function (see
5.2.10/4). I can't imagine a mapping function which would result
in pi having a different value after *both* exclusive ors have
executed, but it is easy to imagine a mapping function which
leaves the intermediate value 'pi ^ 0xaaaaaaaa' pointing to an
unmapped region of memory. There are machines which use special
registers for pointers, and check the validity of a pointer when
it is loaded into such a register. Herb's code, on such a
machine, might fault.
3.7.3.2/4 contains:
# ... The effect of using an invalid pointer value ... is
# undefined. ...
But this could be stretch - 3.7.3.2/4 is about deallocation
functions.
So I have two questions:
(a) Is using a deallocation function to deallocate the object(s)
pointed to the only way to make invalid pointers?
I'm nearly certain the answer to this is no, but I can't find
the standardese which says so.
(b) Since the mapping of int to int* is implementation-defined,
could an implementation map '(int)pi ^ 0xaaaaaaaa' (for
example) to an invalid pointer?
I think yes.
I think Herb is in the land of undefined behavior here, or at best,
depending on an implementation-defined mapping, but I don't
think the standard is clear in this area.
That being said, AFAIK, fault-on-load-of-invalid-pointer enviroments
are rare; while x86 provides the hardware features, they are
rarely used.
It's worth pointing out that there is a doubly-linked list
implementation technique which, rather than store two pointers in
each node, stores only the value of (next ^ previous) in the
pointer, reducing sizeof(node) by sizeof(node*) . Such a list is
traversed forward by maintaing a pointer to the previous node,
which is xored with the stored value (next ^ previous), resulting
in next, and traversed backward in an analogous fashion. (I
haven't seen or used such a beast for a long time, but they do
exist.) So while Herb's example is clearly not real code, there
is real code which has a good reason to rely on the ability to
use the result of xoring pointer values. However, I don't think
it's necessary to store such values in pointers; they could be
stored in an integer of sufficient size just as easily.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Jack Klein Guest
|
Posted: Mon Jul 12, 2004 12:08 am Post subject: Re: casting int to int* |
|
|
On 11 Jul 2004 06:34:09 -0400, [email]oldwolf (AT) inspire (DOT) net.nz[/email] (Old Wolf) wrote
in comp.lang.c++.moderated:
| Quote: | On Herb Sutter's blog he writes:
Consider the following well-formed ISO C++ program
with well-defined semantics:
int* pi = new int(42); // line 1
pi = (int*)((int)pi ^ 0xaaaaaaaa);
// ... do other work ...
pi = (int*)((int)pi ^ 0xaaaaaaaa);
cout << *pi; // perfectly ok, prints "42", won't crash
delete pi; // ok
I thought this was implementation-defined behaviour, and I don't
see how it works in the case of 16bit int but 32bit int *
|
The conversion of a pointer to an integer type, with a suitable case,
may produce undefined behavior if width of the integer type is not
sufficient. On a platform where int has 16 bits and pointers 32, the
behavior is undefined. On the growing number of platforms where an
int is 32 bits but a pointer is 64 bits, the behavior is undefined.
If there is a suitable integer type, conversion of a pointer to the
integer type and back again to the original pointer type will produce
a pointer to the same object that will compare identical to the
original pointer.
So this parlor trick should work on most of today's common desk top
platforms where sizeof(int *) == sizeof(int), and signed int has no
trap representations, but it is implementation-defined and unportable.
The latest C standard (1999) provides an optional typedef for an
itptr_t type, guaranteed to be large enough to hold the value of a
pointer. It is optional because neither C nor C++ requires that any
integer type be as large as a pointer.
Most of the features of the C99 header
added to the next version of the C++ standard.
--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Jul 12, 2004 10:00 pm Post subject: Re: casting int to int* |
|
|
[email]oldwolf (AT) inspire (DOT) net.nz[/email] (Old Wolf) wrote in message
news:<843a4f78.0407101716.24710b6d (AT) posting (DOT) google.com>...
| Quote: | On Herb Sutter's blog he writes:
Consider the following well-formed ISO C++ program
with well-defined semantics:
int* pi = new int(42); // line 1
pi = (int*)((int)pi ^ 0xaaaaaaaa);
// ... do other work ...
pi = (int*)((int)pi ^ 0xaaaaaaaa);
cout << *pi; // perfectly ok, prints "42", won't crash
delete pi; // ok
I thought this was implementation-defined behaviour, and I don't see
how it works in the case of 16bit int but 32bit int *
|
Others have already shown why it is, in fact, undefined behavior. On
the other hand, it is easy to translate it into something equivalent
that is defined:
void
encode( void* p, size_t l )
{
unsigned char* tmp = static_cast< unsigned char* >( p ) ;
while ( l != 0 ) {
*tmp ^= 0xaa ;
++ tmp ;
-- l ;
}
}
int* pi = new int( 42 ) ;
encode( &pi, sizeof( int* ) ) ;
// Do other work, don't touch pi...
encode( &pi, sizeof( int* ) ) ;
cout << *pi ; // perfectly ok, prints 42, won't crash
delete pi ;
I suspect that Herb presented this example in conjunction with
discussions about garbage collection; that's where it usually comes up.
The above example is 100% standard C++, and makes the point he probably
meant to make.
Of course, in real life, programs don't do things like this. They do,
however, memcpy pointers into buffers, which they write to disk or
transmit accross the network, they receive back and use. In practice,
it is not a very good technic, because lf a lack of robustness, but
there are programs out there that aren't always as robust as they should
be. Also, I find it difficult to imagine a case where one wouldn't also
keep a local copy of the pointer. But the standard doesn't require it.
--
James Kanze GABI Software http://www.gabi-soft.fr
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
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Balog Pal Guest
|
Posted: Wed Jul 14, 2004 9:09 pm Post subject: Re: casting int to int* |
|
|
<kanze (AT) gabi-soft (DOT) fr> wrote
| Quote: | Of course, in real life, programs don't do things like this. They do,
however, memcpy pointers into buffers, which they write to disk or
transmit accross the network, they receive back and use. In practice,
it is not a very good technic, because lf a lack of robustness, but
there are programs out there that aren't always as robust as they should
be. Also, I find it difficult to imagine a case where one wouldn't also
keep a local copy of the pointer. But the standard doesn't require it.
|
A not so exotic example is the windows message where lParam is very
often a pointer on both win16 and win32.
Or the ItemData member of several UI elements (listbox, combobox, listview,
treeview)which is also a DWORD (32 bit unsigned integer) is used to hold
pointer to something.
If Herb's original point was making pointers disappearing from sight of the
GC collector, well, windows programs do that all the time without xoring.
Paul
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Allan W Guest
|
Posted: Tue Jul 20, 2004 8:00 am Post subject: Re: casting int to int* |
|
|
llewelly <llewelly.at (AT) xmission (DOT) dot.com> wrote
| Quote: | It's worth pointing out that there is a doubly-linked list
implementation technique which, rather than store two pointers in
each node, stores only the value of (next ^ previous) in the
pointer, reducing sizeof(node) by sizeof(node*) . Such a list is
traversed forward by maintaing a pointer to the previous node,
which is xored with the stored value (next ^ previous), resulting
in next, and traversed backward in an analogous fashion. (I
haven't seen or used such a beast for a long time, but they do
exist.) So while Herb's example is clearly not real code, there
is real code which has a good reason to rely on the ability to
use the result of xoring pointer values.
|
The main purposes of such code would be:
(1) Reducing memory footprint, but only when you use an allocation
technique that guarantees a maximum distance between nodes.
(2) Example code that demonstrates why even "conservative garbage
collection" techniques can destroy memory still in use.
(The latter purpose is a little bit like driving a car off a cliff,
to prove that cars aren't safe...)
| Quote: | However, I don't think
it's necessary to store such values in pointers; they could be
stored in an integer of sufficient size just as easily.
|
In fact, this might even be easier. Languages that don't have pointers,
have long been able to create "linked lists" by using integer offsets
into an array.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|