Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strict aliasing in relation to aggregate or union types

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.

like image 865
mshildt Avatar asked Feb 10 '15 14:02

mshildt


2 Answers

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.

like image 136
Persixty Avatar answered Oct 02 '22 15:10

Persixty


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.

like image 45
Agnius Vasiliauskas Avatar answered Oct 02 '22 16:10

Agnius Vasiliauskas