According to the C Standard:
Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.
As such, can one safely cast a _Complex float *
into a float *
and then access the real/imaginary components using the float *
? Or will that still technically violate strict-aliasing and therefore invoke undefined behavior?
_Complex float c;
((float *)&c)[0] = 1;
((float *)&c)[1] = 2;
c; // 1 + 2i
C 2018 6.2.5 13 and C 2024 6.2.5 say:
Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.
This guarantees that you could use memcpy
to copy the bytes representing an array of 2 N float
objects to the bytes of an array of N complex float
objects, or vice-versa, and get the naturally expected results: Because they have the same representation in memory, and the C standard supports reading and updating the bytes that represent objects through memcpy
or other character-copying mechanisms, this will reproduce the values.
It is less clear this means you can alias the same memory using the different types which is what you do by converting a pointer and dereferencing in the new type. Formally, this does not conform to the aliasing rules in C 2018 6.5 7 or C 2024 6.5. However, in discussing another case in which the standard imposes requirements that representations be identical, C 2018 6.2.5 9 and C 2024 6.2.5 say in non-normative note 41 (C 2018) and 34 (C 2024):
The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
This phrasing about the “same representation” is used multiple times in the standard with the intent, according to notes, that it makes various pairs of types interchangeable in some vague way that the standard does not define. This conflicting information between aliasing and interchangeability is a long-standing defect in the C standard, and one might interpret the interchangeability as overriding or coming before the aliasing rules. That is, the authors of the standard wanted us to be able to alias things that were required to have the same representation and alignment requirements but the committee did not get around to writing formal rules for that. However, that interpretation cannot be said to be a formal statement of the C standard, and therefore it is not strictly guaranteed that aliasing a complex object as an array of two real elements is defined.
In my experience with various compilers, they will support this particular aliasing. Optimizations will be made assuming, for example, that int
and float
types do not alias the same memory but not that “array reshaping” (reinterpreting a multidimensional array as an array of other dimensions) will not occur, including this treatment of complex double
objects and arrays of double
. If you are writing academic software to study some scientific models and the risk of being wrong about this interpretation of the C standard is that wrong answers might be produced, this might be a risk you are willing to take. If you are writing safety-critical software, you should not rely on this interpretation without assertions from the compiler developers.