I know that the following is explicitly allowed in the standard:
int n = 0;
char *ptr = (char *) &n;
cout << *ptr;
What about this?
alignas(int) char storage[sizeof(int)];
int *ptr = (int *) &storage[0];
*ptr = 0;
cout << *ptr;
Essentially, I'm asking if the aliasing rules allow for a sequence of chars to be accessed through a pointer to another type. I'd like references to the portions of the standard that indicate one way or another if possible.
Some parts of the standard have left me conflicted; (3.10.10) seems to indicate it would be undefined behavior on the assumption that the dynamic type of storage
is not int
. However, the definition of dynamic type is unclear to me, and the existence of std::aligned_storage
would lead me to believe that this is possible.
The code
int *ptr = (int *) &storage[0]; *ptr = 0;
causes undefined behaviour by violating the strict aliasing rule (C++14 [basic.lval]/10)The objects being accessed have type
char
but the glvalue used for the access has typeint
.The "dynamic type of the object" for a
char
is stillchar
. (The dynamic type only differs from the static type in the case of a derived class). C++ does not have any equivalent of C's "effective type" either, which allows typed objects to be "created" by using the assignment operator into malloc'd space.Regarding correct use of
std::aligned_storage
, you're supposed to then use placement-new to create an object in the storage. The use of placement-new is considered to end the lifetime of thechar
(or whatever) objects, and create a new object (of dynamic storage duration) of the specified type, re-using the same storage. Then there will be no strict aliasing violation.You could do the same thing with the char array, e.g.:
Note that no pseudo-destructor call or
delete
is required for built-in typeint
. You would need to do that if using a class type with non-trivial initialization. Link to further readingwrites to an int, so it is an access to int, with an lvalue of type int, so that part of the code is fine.
The cast is morally fine, but the C/C++ standard texts don't clearly describe casts, or pointers, or anything fundamental.
The
union
construct might be useful here.union
is similar tostruct
, except that all of the elements of aunion
occupy the same area of storage.They are, in other words, "different ways to view the same thing," just like FORTRAN's
EQUIVALENCE
declaration. Thus, for instance:offers three entirely-different ways to consider the same area of storage. (The storage-size of a
union
is the size of its longest component.)foo
,bar
, andbletch
are all synonyms for the same storage.union
is often used withtypedef
, as illustrated in this StackOverflow article: C: typedef union.