In the old days of pre-ISO C, the following code would have surprized nobody:
struct Point {
double x;
double y;
double z;
};
double dist(struct Point *p1, struct Point *p2) {
double d2 = 0;
double *coord1 = &p1.x;
double *coord2 = &p2.x;
int i;
for (i=0; i<3; i++) {
double d = coord2[i] - coord1[i]; // THE problem
d2 += d * d;
return sqrt(d2);
}
At that time, we all knew that alignment of double allowed the compiler to add no padding in struct Point
, and we just assumed that pointer arithmetics would do the job.
Unfortunately, this problematic line uses pointer arithmetics (p[i]
being by definition *(p + i)
) outside of any array which is explicitely not allowed by the standard. Draft n1570 for C11 says in 6.5.6 additive operators §8:
When an expression that has integer type is added to or subtracted from a pointerpointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression...
As nothing is said when we have not two elements of the same array, it is unspecified by the standard and from there Undefined Behaviour (even if all common compilers are glad with it...)
Question:
As this idiom allowed to avoid code replication changing just x
with y
then z
which is quite error prone, what could be a conformant way to browse the elements of a struct as if they were members of the same array?
Disclaimer: It obviously only applies to elements of same type, and padding can be detected with a simple static_assert
as shown in that other question of mine, so padding, alignment and mixed types are not my problem here.
C does not define any way to specify that the compiler must not add padding between the named members of
struct Point
, but many compilers have an extension that would provide for that. If you use such an extension, or if you're just willing to assume that there will be no padding, then you could use aunion
with an anonymous innerstruct
, like so:You can then access the coordinates by their individual names or via the
coords
array:This is mainly a complement to JohnBollinger's answer. Anonymous struct members do allow a clean and neat syntax, and C defines a union as a type consisting of a sequence of members whose storage overlap (6.7.2.1 Structure and union specifiers §6). Accessing a member of a union is then specified in 6.5.2.3 Structure and union members:
and the (non normative but informative) note 95 precises:
That means that for the current version of the standard the aliasing of a struct by an array with the help of anonymous struct member in a union is explicitly defined behaviour.