I am using a library which represents an array of 3D Cartesian vectors as sequential one-dimensional array of double
s, where three subsequent entries correspond to the Cartesian components of one vector. I also have a self-written class Vector3
for 3D vectors which implements some arithmetics. I would like to treat this plain double
array as an array of Vector3
objects instead, as in the following:
class Vector3 {
// Some functions and operators
double m_pData[3]; // the components of the vector
};
double *pd = GetFromLibrary(); // The input raw data
Vector3 *pv = (Vector3*)pd; // Treat raw data as array of objects...
pv[3].m_pData[1] = 17.5; // Set the Y component of the fourth vector
I tried this code on several different compilers and platforms, and it works fine. From a naive view, it should of course always work, but I am not sure if there could arise any alignment issues. Does the C++ standard state that this is allowed? If not, is there any way how I could modify the code to make it standard conforming without copying the data to a different memory location?
Or to ask in a different way: Does the standard guarantee that a plain array of Vector3
has all of its double
data members without gaps, our could it happen that the compiler inserts some padding for alignment (maybe to multiples of 32 bytes)?
Even if alignment and size/layout are correct, it is technically still UB per standard. You can't cast to some type (here Vector3
) pretending that an object of that type exists when it was never explicitly or implicitly created. The result will instead be a pointer that still points to the original double
object, but with mismatched expression type, so that any member access will have undefined behavior, as will any pointer arithmetic such as in pv[3]
. In practice compilers probably won't care that much (but neither do I know whether they are committed to allow such a use, now or in the future).
For alignment itself: The alignment requirement of Vector3
may technically be stricter than that of double
, so that the pointer returned by GetFromLibrary
may be misaligned. That's something to look up in the ABI specification you are relying on or by adding static_assert(alignof(Vector3) <= alignof(double));
. However, I don't think any ABI in common use does that.
For other layout issues: Technically there can be padding at the end of Vector3
. However, again, I am not aware of any ABI that behaves like this. And you can verify this too with static_assert(sizeof(Vector3) == sizeof(double)*3);
. Padding before the array member or inbetween array elements is not permitted.
Note that I assume in the above that your class is standard-layout and doesn't have any further non-static data members or any base classes. This also means no virtual
member functions, etc.
If that is not the case, then there can also be padding before the array member and it is much less likely that size and alignment will be correct (especially if there are virtual
functions). Again, the concrete layout will be specified either way in the applicable ABI specification.
Also, if instead of using the same scalar type (double
) in both instances, you used mismatched scalar types (e.g. double
in Vector3
, but pointer into a uint64_t
array), then compilers will not be lenient. This would be a direct aliasing violation that is itself UB and compilers do optimize on, so that behavior will silently not be as expected.