Search code examples
c++memorystrict-aliasingtype-punning

How does C++ strict aliasing work with arrays and dynamically allocated memory?


I could not find a lot of resources like this that cover all of the edge cases of the strict aliasing rule. If I understand correctly, in C++ it is UB to access an object that does not exist at a particular location (for example cast of uint32_t* to float* and access is illegal since there is no float object alive at that address), and the only way to start a lifespan of such an object is using placement new and a simple assignment does not suffice. This brings me to my question:

// CASE 1
uint64_t arr[8] = {}; //dynamic type of each element now uint64_t?

for(int ii = 0; ii < 8; ++ii){
    new (arr+ii) double(3.14); // dynamic type now double?
}

arr[0] = 10; // dereference as uint64_t while 
             //dynamic type is double? so this violates strict aliasing I assume?


// CASE 2
uint64_t* arr = new uint64_t[8]; //dynamic type of each element now uint64_t?

// CASE 3
uint64_t* mal_arr = (uint64_t*)malloc(8*sizeof(uint64_t)); // dynamic type now void? 

mal_arr[0] = 10; // if it is indeed void, this must be illegal, correct?
                                  

Solution

  • Case 1:

    Firstly, I'll assume that alignof(double) >= alignof(uint64_t). Otherwise the very first placement-new is UB.

    Next, from what I can tell, arr+ii on the second iteration is UB, because + can only move a pointer between elements of the same array, and the lifetime of the array ends once you placement-new the first double on top of it. But this is mostly a theoretical issue, + should work correctly in practice.

    Then arr[0] = 10; is an outright strict aliasing violation, causing UB.

    Case 3:

    This used to be UB (placement-new is missing), but C++20 gave us implicit-lifetime types, which can be created implicitly by malloc and some other methods.