I\'m in the middle of a discussion trying to figure out whether unaligned access is allowable in C++ through reinterpret_cast
. I think not, but I\'m having tro
EDIT This answers the OP's original question, which was "is accessing a misaligned pointer safe". The OP has since edited their question to "is dereferencing a misaligned pointer safe", a far more practical and less interesting question.
The round-trip cast result of the pointer value is unspecified under those circumstances. Under certain limited circumstances (involving alignment), converting a pointer to A to a pointer to B, and then back again, results in the original pointer, even if you didn't have a B in that location.
If the alignment requirements are not met, than that round trip -- the pointer-to-A to pointer-to-B to pointer-to-A results in a pointer with an unspecified value.
As there are invalid pointer values, dereferencing a pointer with an unspecified value can result in undefined behavior. It is no different than *(int*)0xDEADBEEF
in a sense.
Simply storing that pointer is not, however, undefined behavior.
None of the above C++ quotes talk about actually using a pointer-to-A as a pointer-to-B. Using a pointer to the "wrong type" in all but a very limited number of circumstances is undefined behavior, period.
An example of this involves creating a std::aligned_storage_t<sizeof(T), alignof(T)>
. You can construct your T
in that spot, and it will live their happily, even though it "actually" is an aligned_storage_t<sizeof(T), alignof(T)>
. (You may, however, have to use the pointer returned from the placement new
for full standard compliance; I am uncertain. See strict aliasing.)
Sadly, the standard is a bit lacking in terms of what object lifetime is. It refers to it, but does not define it well enough last I checked. You can only use a T
at a particular location while a T
lives there, but what that means is not made clear in all circumstances.
All of your quotes are about the pointer value, not the act of dereferencing.
5.2.10, paragraph 7 says that, assuming int
has a stricter alignment than char
, then the round trip of char*
to int*
to char*
generates an unspecified value for the resulting char*
.
On the other hand, if you convert int*
to char*
to int*
, you are guaranteed to get back the exact same pointer as you started with.
It doesn't talk about what you get when you dereference said pointer. It simply states that in one case, you must be able to round trip. It washes its hands of the other way around.
Suppose you have some ints, and alignof(int) > 1
:
int some_ints[3] ={0};
then you have an int pointer that is offset:
int* some_ptr = (int*)(((char*)&some_ints[0])+1);
We'll presume that copying this misaligned pointer doesn't cause undefined behavior for now.
The value of some_ptr
is not specified by the standard. We'll be generous and presume it actually points to some chunk of bytes within some_bytes
.
Now we have a int*
that points to somewhere an int
cannot be allocated (3.11/1). Under (3.8) the use of a pointer to an int
is restricted in a number of ways. Usual use is restricted to a pointer to an T
whose lifetime has begun allocated properly (/3). Some limited use is permitted on a pointer to a T
which has been allocated properly, but whose lifetime has not begun (/5 and /6).
There is no way to create an int
object that does not obey the alignment restrictions of int
in the standard.
So the theoretical int*
which claims to point to a misaligned int does not point to an int. No restrictions are placed on the behavior of said pointer when dereferenced; usual dereferencing rules provide behavior of a valid pointer to an object (including an int
) and how it behaves.
And now our other assumptions. No restrictions on the value of some_ptr
here are made by the standard: int* some_ptr = (int*)(((char*)&some_ints[0])+1);
.
It is not a pointer to an int
, much like (int*)nullptr
is not a pointer to an int
. Round tripping it back to a char*
results in a pointer with unspecified value (it could be 0xbaadf00d
or nullptr
) explicitly in the standard.
The standard defines what you must do. There are (nearly? I guess evaluating it in a boolean context must return a bool) no requirements placed on the behavior of some_ptr
by the standard, other than converting it back to char*
results in an unspecified value (of the pointer).
Looking at 3.11/1:
Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated.
There's some debate in comments about exactly what constitutes allocating an object of a type. However I believe the following argument works regardless of how that discussion is resolved:
Take *reinterpret_cast<uint32_t*>(a)
for example. If this expression does not cause UB, then (according to the strict aliasing rule) there must be an object of type uint32_t
(or int32_t
) at the given location after this statement. Whether the object was already there, or this write created it, does not matter.
According to the above Standard quote, objects with alignment requirement can only exist in a correctly aligned state.
Therefore any attempt to create or write an object that is not correctly aligned causes UB.