问题
Is the following well-defined:
char* charPtr = new char[42];
int* intPtr = (int*)charPtr;
charPtr++;
intPtr = (int*) charPtr;
The intPtr
isn't properly aligned (in at least one of the two cases). Is it illegal just having it there? Is it UB using it at any stage? How can you use it and how can't you?
回答1:
First, of course: the pointer is guaranteed to be aligned in the
first case (by §5.3.4/10 and §3.7.4.1/2), and may be correctly
aligned in both cases. (Obviously, if sizeof(int) == 1
, but
even when this is not the case, an implementation doesn't
necessarily have alignment requirements.)
And to make things clear: your casts are all reinterpret_cast
.
Beyond that, this is an interesting question, because as far as
I can tell, there is no difference in the two casts, as far as
the standard is concerned. The results of the conversion are
unspecified (according to §5.2.10/7); you're not even guaranteed
that converting it back into a char*
will result in the
original value. (It obviously won't, for example, on machines
where int*
is smaller than a char*
.)
In practice, of course: the standard requires that the return
value of new char[N]
be sufficiently aligned for any value
which may fit into it, so you are guaranteed to be able to do:
intPtr = new (charPtr) int;
Which has exactly the same effect as your cast, given that the
default constructor for int
is a no-op. (And assuming that
sizeof(int) <= 42
.) So it's hard to imagine an implementation
in which the first part fails. You should be able to use the
intPtr
just like any other legally obtained intPtr
. And the
idea that converting it back to a char*
would somehow result
in a different value from the original char*
seems
preposterous.
In the second part, all bets are off: you definitely can't
dereference the pointer (unless your implementation guarantees
otherwise), and it's also quite possible that converting it back
to char*
results in something different. (Imagine a word
addressed machine, for example, where converting a char*
to an
int*
rounds up. Then converting back would result in
a char*
which was sizeof(int)
higher than the original. Or
where an attempt to convert a misaligned pointer always resulted
in a null pointer.)
回答2:
In general, the result is unspecified (5.2.10p7) if the alignment requirements of int
are greater than those of char
, which they usually will be. The result will be a valid value of the type int *
so it can be e.g. printed as a pointer with operator<<
or converted to intptr_t
.
Because the result has an unspecified value, unless specified by the implementation it is undefined behaviour to indirect it and perform lvalue-to-rvalue conversion on the resulting int
lvalue (except in unevaluated contexts). Converting back to char *
will not necessarily round-trip.
However, if the original char *
was itself the result of a cast from int *
, then the cast to int *
counts as the second half of a round trip; in that case, the cast is defined.
In particular, in the case above where the char *
was the result of a new[]
expression, we are guaranteed (5.3.4p10) that the char *
pointer is appropriately aligned for int
, as long as sizeof(int) <= 42
. Because the new[]
expression obtains its storage from an allocation function, 3.7.4.1p2 applies; the void *
pointer can be converted
to a pointer of any complete object type with a fundamental alignment requirement and then used to access the object [...] which strongly implies, along with the note to 5.3.4p10, that the same holds for the char *
pointer returned by the new[]
expression. In this case the int *
is a pointer to an uninitialised int
object, so performing lvalue-to-rvalue conversion on its indirection is undefined (3.8p6), but assigning to its indirection is fully defined. The int
object is in the storage allocated (3.7.4.1p2) so converting the int *
back to char *
will yield the original value per 1.8p6. This does not hold for the incremented char *
pointer as unless sizeof(int) == 1
it is not the address of an int
object.
来源:https://stackoverflow.com/questions/14773127/casting-between-primitive-type-pointers