Just a couple weeks ago, I learned that the C++ Standard had a strict aliasing rule. Basically, I had asked a question about shifting bits -- rather than shifting each byte one at a time, to maximize performance I wanted to load my processor's native register's with (32 or 64 bits, respectively) and perform the shift of 4/8 bytes all in a single instruction.
This is the code I wanted to avoid:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
for (int i = 0; i < 3; ++i)
{
buffer[i] <<= 4;
buffer[i] |= (buffer[i + 1] >> 4);
}
buffer[3] <<= 4;
And instead, I wanted to use something like:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
unsigned int *p = (unsigned int*)buffer; // unsigned int is 32 bit on my platform
*p <<= 4;
Someone called out in a comment that my proposed solution violated the C++ Aliasing rules (because p was of type int*
and buffer was of type char*
and I was dereferencing p to perform the shift. (Please ignore possible issues of alignment and byte order -- I handle those outside of this snippet) I was quite surprised to learn about he Strict Aliasing rule since I regularly operate on data from buffers, casting it from one type to another and have never had any issue. Further investigation revealed that the compiler I use (MSVC) doesn't enforce strict aliasing rules and since I only develop on gcc/g++ in my spare time as a hobby, I likely just hadn't encountered the issue yet.
So then I asked a question about Strict Aliasing Rules and C++'s Placement new operator:
IsoCpp.org offers a FAQ regarding placement new and they provide the following code example:
#include <new> // Must #include this to use "placement new"
#include "Fred.h" // Declaration of class Fred
void someCode()
{
char memory[sizeof(Fred)]; // Line #1
void* place = memory; // Line #2
Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below)
// The pointers f and place will be equal
// ...
}
The example is simple enough, but I'm asking myself, "What if someone calls a method on f
-- e.g. f->talk()
? At that point we would be dereferencing f
, which points to the same memory location as memory
(of type char*
. I've read numerous places that there is an exemption for variables of type char*
to alias any type, but I was under the impression that it wasn't a "two-way street" -- meaning, char*
can alias (read/write) any type T
, but type T
can only be used to alias a char*
if T
itself is of char*
. As I'm typing this, that doesn't make any sense to me and so I'm leaning towards the belief that the claim that my initial (bit shifting example) violated the strict aliasing rule is false.
Can someone please explain what is correct? I've been going nuts with trying to understand what is legal and what is not (despite having read numerous websites and SO posts on the topic)
Thank you
f
were undefined behavior, that would make placement new kind of useless wouldn't it? – Saulsmemory
is notchar *
. On line 2 the array decays to a pointer, but that doesn't meanmemory
is a pointer. And, even ifmemory
was a pointer, the type of the memory location it pointed to would bechar
, notchar *
. – Threemasterf->talk()
is OK; I think it would improve the question to delete all the preamble (the stuff before "So then") – Tersina