Mark P posted:
In your specific example I believe that this is *not* the case,
however, since your conversion sequence is: int* -> void* -> char* ->
void* -> int*.
The point of my code is that I go from T* to char* and then back to T*. I
draw an analogy with other types:
double a = 56.253; /* Here's our original value */
int b = a; /* We store it in a different type */
double c = b; /* Now we bring it back to the original type */
assert( a == c ); /* Will the value have been preserved? */
In the example immediately above, "information will be lost" when we go
from double to int. Even though we finally go back to double, the
"corruption" has already taken place. Now let's look at it with pointers:
int a;
int *pint = &a; /* Here's our original value */
void *pvoid = pint; /* We store it in a different type */
int *pint2 = pvoid; /* We bring it back to the original type */
assert( pint2 == pint ); /* Will the value have been preserved? */
The above code snippet is guaranteed to work because you can store any
object's address in a "void*" and there won't be any "corruption".
As you quoted several times, the Standard also specifies that you can
reliably go from T1* to T2* without "corruption", but only if the
alignment requirements of T2 are no stricter. As "char" is the smallest
and most simple type we have in C++, it should have the least alignment
requirements (if not none). Therefore the conversion from any legitimate
pointer value to "char*" should go off without a hitch. Example:
int a;
int *pint = &a; /* Here's our original value */
char *pchar =
static_cast<char*>(pint); /* We store it in a different type */
int *pint2 =
static_cast<int*>(pchar); /* We bring it back to the original type */
assert( pint2 == pint ); /* Will the value have been preserved? */
The above should be perfectly okay.
You have gone on to say, notwithstanding any of the above, that the
conversion from "void*" to "char*" may be unspecified. However, if you
consider that a "void*" (assuming it contains a legitimate address) had
to start off as some other pointer value, you can see how there should be
no problem with going to "char*", given that the original pointer value
would have been able to go directly to "char*". That is to say, if the
following is possible:
T* to char*
Then the following should also be possible:
T* to void* to char*
In particular, your conversion sequence "unwinds"
itself and retraces its steps back to the original type.
But as I demonstrated with my "double" example, the "corruption" has
already taken place.
Suppose instead you had offered:
int main ()
{
int val = 0;
void* pv = &val;
"pv" should hold val's address without any corruption.
char* pc = static_cast<char*>(pv);
"pc" should hold the address stored in pv without any corruptino.
int* pi = static_cast<int*> (pc);
Back to the original type. Shouldn't be any corruption.
*pi = 7; // now, what is val?
Should work perfectly.
However logical it may seem that val should be 7, the standard
nonetheless indicates that the value of val is unspecified.
I suppose we have to decide just how pedantic the Standard has to be.
Should it be enough for us to presume that it works (because there's
about ten voices in my head shouting "For God's sake it works!"), or
should we be thinking, "The Standard has to state it explicitly in plain
English"?
Only you know what you mean by reliably.
reliably = no corruption, the original value is preserved perfectly.
And where did you get the idea that the standard always makes sense?
Sometimes that's the only hope we have.
It's clearly not illegal. Depending how it's used it may be
unspecified (though it's hard to imagine that an implementation would
go out of its way to make this not work as one would assume).
I would never thing twice about any "dangers" of using "char*". I see it
as a "universal pointer type", just like "void*".
-Tomás