I'm trying to understand the implications of the following statement in the C99 standard (C99; ISO/IEC 9899:1999 6.5/7)
An object shall have its stored value accessed only by an lvalue expression that has one of the following types 73) or 88):
(other statements unrelated to question omitted)
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)
Consider the following:
typedef struct MyComplex {
float real;
float imag;
} MyComplex;
MyComplex *carray = malloc(sizeof(*carray) * 10);
float *as_floats = (float *)carray;
Is this legal since the MyComplex
structure contains a compatible float
type for the as_floats
pointer?
What about the other way around? i.e:
float *farray = malloc(sizeof(*farray) * 10);
MyComplex *as_complex = (MyComplex *)farray;
In both cases, ultimately, everything we're dealing with here is a float
, so maybe it's ok? I'm just not sure.
I ask, because I'm dealing with a legacy code base that does this kind of stuff all over the place and so far, everything seems fine. But I feel like we're playing with fire here. Trying to figure out if I need to disable strict aliasing on the compiler command line or not.
Note 13 in 6.7.2.1 of (I believe) all versions of the standard condones case #1 explicitly. You'll rarely get a clearer answer! My emphasis
Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
http://port70.net/~nsz/c/c99/n1256.html#6.7.2.1
YES! You can cast a structure to access its first member directly!
Why anyone thinks that's better than &(carray->real)
isn't clear. But it's definitely legit.
As another commenter pointed out I discussed case #2 in a previous question.
Aliasing Arrays through structs
It appears in conclusion that case #2 is an OK way to access the member real
.
Also iff the structure has no internal padding (platform dependent) you could even access the second member of the array through imag
.
I say platform dependent but other investigations have offered no known platform where such a structure would include padding.
I've tried to reproduce some problems with using this casting approach. One problem is when struct uses bit fields and another - when there is compiler directive which forces struct members to be aligned to some size:
// issue 1 : problem with bitFields
typedef struct MyComplex_1 {
unsigned int real : 1;
unsigned int imag : 2;
} MyComplex_1;
// issue 2 : problem with members forced alignment
typedef struct MyComplex_2 {
unsigned int real;
unsigned int imag;
} __attribute__ ((aligned (64))) MyComplex_2; // GCC compiler directive
int main()
{
MyComplex_1 arr_1[] = {{1, 2}, {1, 2}};
MyComplex_2 arr_2[] = {{1, 2}, {1, 2}};
int i;
// for bitfield issue
for (i=0; i<2; i++)
printf("struct (%d,%d)\n", arr_1[i].real, arr_1[i].imag);
for (i=0; i<4; i++)
printf("var (%d)\n", ((unsigned int*)arr_1)[i]);
// for struct member forced alignement issue
for (i=0; i<2; i++)
printf("struct (%d,%d)\n", arr_2[i].real, arr_2[i].imag);
for (i=0; i<4; i++)
printf("var (%d)\n", ((unsigned int*)arr_2)[i]);
return 0;
}
There may be more problems, but in my opinion this thing is risky.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With