My company uses a messaging server which gets a message into a const char*
and then casts it to the message type.
I've become concerned about this after asking this question. I'm not aware of any bad behavior in the messaging server. Is it possible that const
variables do not incur aliasing problems?
For example say that foo is defined in MessageServer
in one of these ways:
void MessageServer(const char* foo)
MessageServer
: const char* foo = PopMessage();
Now MessageServer
is a huge function, but it never assigns anything to foo
, however at 1 point in MessageServer
's logic foo
will be cast to the selected message type.
auto bar = reinterpret_cast<const MessageJ*>(foo);
bar
will only be read from subsequently, but will be used extensively for object setup.
Is an aliasing problem possible here, or does the fact that foo
is only initialized, and never modified save me?
EDIT:
Jarod42's answer finds no problem with casting from a const char*
to a MessageJ*
, but I'm not sure this makes sense.
We know this is illegal:
MessageX* foo = new MessageX;
const auto bar = reinterpret_cast<MessageJ*>(foo);
Are we saying this somehow makes it legal?
MessageX* foo = new MessageX;
const auto temp = reinterpret_cast<char*>(foo);
auto bar = reinterpret_cast<const MessageJ*>(temp);
My understanding of Jarod42's answer is that the cast to temp
makes it legal.
EDIT:
I've gotten some comments with relation to serialization, alignment, network passing, and so on. That's not what this question is about.
This is a question about strict aliasing.
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias eachother.)
What I'm asking is: Will the initialization of a const
object, by casting from a char*
, ever be optimized below where that object is cast to another type of object, such that I am casting from uninitialized data?
First of all, casting pointers does not cause any aliasing violations (although it might cause alignment violations).
Aliasing refers to the process of reading or writing an object through a glvalue of different type than the object.
If an object has type T
, and we read/write it via a X&
and a Y&
then the questions are:
X
alias T
?Y
alias T
?It does not directly matter whether X
can alias Y
or vice versa, as you seem to focus on in your question. But, the compiler can infer if X
and Y
are completely incompatible that there is no such type T
that can be aliased by both X
and Y
, therefore it can assume that the two references refer to different objects.
So, to answer your question, it all hinges on what PopMessage
does. If the code is something like:
const char *PopMessage()
{
static MessageJ foo = .....;
return reinterpret_cast<const char *>(&foo);
}
then it is fine to write:
const char *ptr = PopMessage();
auto bar = reinterpret_cast<const MessageJ*>(foo);
auto baz = *bar; // OK, accessing a `MessageJ` via glvalue of type `MessageJ`
auto ch = ptr[4]; // OK, accessing a `MessageJ` via glvalue of type `char`
and so on. The const
has nothing to do with it. In fact if you did not use const
here (or you cast it away) then you could also write through bar
and ptr
with no problem.
On the other hand, if PopMessage was something like:
const char *PopMessage()
{
static char buf[200];
return buf;
}
then the line auto baz = *bar;
would cause UB because char
cannot be aliased by MessageJ
. Note that you can use placement-new to change the dynamic type of an object (in that case, char buf[200]
is said to have stopped existing, and the new object created by placement-new exists and its type is T
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With