32
votes

When I compile this sample code using g++, I get this warning:

warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

The code:

#include <iostream>

int main() 
{
   alignas(int) char data[sizeof(int)];
   int *myInt = new (data) int;
   *myInt = 34;

   std::cout << *reinterpret_cast<int*>(data);
}

In this case, doesn't data alias an int, and therefore casting it back to an int would not violate strict aliasing rules? Or am I missing something here?

Edit: Strange, when I define data like this:

alignas(int) char* data = new char[sizeof(int)];

The compiler warning goes away. Does the stack allocation make a difference with strict aliasing? Does the fact that it's a char[] and not a char* mean it can't actually alias any type?

3
@molbdnilo char * can always aliasShafik Yaghmour
@ShafikYaghmour Yes, of course. How could I forget?molbdnilo
Possibly because data is already an alias for &data[0]? Also int const * data; is a closer match to int data[1];rafeek
You might want to consider using std::aligned_storage for this: en.cppreference.com/w/cpp/types/aligned_storagemattnewport
Why does the warning completely go away since gcc 7.2? LIVE(godbolt.org/g/ci5dKj)sandthorn

3 Answers

23
votes

The warning is absolutely justified. The decayed pointer to data does not point to an object of type int, and casting it doesn't change that. See [basic.life]/7:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
(7.1) — [..]
(7.2) — the new object is of the same type as the original object (ignoring the top-level cv-qualifiers),

The new object is not an array of char, but an int. P0137, which formalizes the notion of pointing, adds launder:

[ Note: If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling std::launder (18.6 [support.dynamic]). — end note ]

I.e. your snippet can be corrected thusly:

std::cout << *std::launder(reinterpret_cast<int*>(data));

.. or just initialize a new pointer from the result of placement new, which also removes the warning.

0
votes

*myInt = 34; this expression is well-formed, because data provide storage for object of type int and myInt is a pointer to an object of type int. So, dereference such a pointer can access an object of type int.

For *reinterpret_cast<int*>(data); this expression, it would violate the strict pointer aliasing. Firstly, there's an array-to-pointer conversion that applied to data, The result is a pointer to the initial element of data,it means the operand of reinterpret_cast<int*> is a pointer to a subject of data.
According to the following rule:

If an object is created in storage associated with a member subobject or array element e, the created object is a subobject of e's containing object if:

  • the lifetime of e's containing object has begun and not ended, and
  • the storage for the new object exactly overlays the storage location associated with e, and
  • the new object is of the same type as e (ignoring cv-qualification).

An object of type int satisfy none of these rules. Hence, the operand of reinterpret_cast<int*> is not a pointer to an object that pointer-interconvertible with an object of type int. So, The result of reinterpret_cast<int*> is not an pointer to an object of type int.

a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object.
  • [...]
-3
votes

What about changing

std::cout << *reinterpret_cast<int*>(data);

to

int *tmp   = reinterpret_cast<int*>(data);
std::cout << *tmp;

?

In my case it got rid of the warning.