Aliasing array with pointer-to-struct without viol

2020-07-09 02:53发布

问题:

Reading this I understood that you can alias structures (without violating the standard, that is) if they have compatible members, i.e given the following struct:

typedef struct {
    uint32_t a;
    uint32_t b;
} Frizzly;

The following would break aliasing rules:

uint32_t foo(uint16_t *i) {
    Frizzly *f = (Frizzly *)i;
    return f->a;
}

But the following would not:

uint32_t foo(uint32_t *i) {
    Frizzly *f = (Frizzly *)i;
    return f->b;
}

because the "aggregrate type" in question contains types compatible with the pointer that that we're casting into it, i.e. a pointer to type uint32_t can be casted into a struct that contains members (or a member) of type uint32_t without breaking aliasing rules.

First, did I understand this correctly?

Secondly, does the ordering and types of the (other) variables within the struct matter? Say, if Frizzly was defined as follows:

typedef struct {
    uint16_t b[2];
    uint32_t a;
}

After the cast in the second example, b is now backed by memory of incompatible (uint32_t) type. Is the cast still valid (or rather, accessing the values through the casted pointer)? Will changes to either element of a alter the value of first element of i (and the other way around) as though strict aliasing were disabled?

Also, if the above is valid, what if I had a struct like so:

typedef struct {
    void *m;
    uint16_t hooah[4];
} Bar;

The following cast would, if I'm correct, break aliasing rules:

void test(char *boo, size_t dee) {
    Bar *bar = (Bar *)(boo + dee);
    do_other_stuff(bar);
}

Could I make the cast valid simply by adding a single unsigned char member into the struct? In other words, casting pointers of incompatible types generally breaks aliasing rules, but since a cast from a pointer to a struct containing a member of type X into a pointer to X is an exception, can any cast from pointer-to-X to aggregrate-Y made valid simply by adding a (possibly dummy) member of type X into Y?

(I didn't actually test the above code snippets in a compiler.)

EDIT:

I know my wording and examples might be rather poor, so I'll try to rephrase the question: if I understood correctly, it is legal for a pointer-to-struct to alias an array of elements of type 'X' as long as the struct contains members of type 'X'. Now, when dereferencing a member of the struct, does the member have to be of the type 'X', or are exceptions to the strict aliasing rules made for all members of the struct regardless of their types as long as there is one member of the appropriate type?

回答1:

According to ISO/IEC9899/TC2 section 6.7.2.1, paragraph 13:

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

So as long as you are casting the struct pointer to the pointer type of the first member it should not violate strict aliasing (specified in section 6.5 paragraph 7) an element may also be accessed via

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)

but this only works in the other direction (accessing a member via a struct pointer, not accessing a struct via a member pointer)



回答2:

None of your examples break the rules, even the cast of a char array to a struct pointer.
All you need to care about is:

  • Array is big enough
  • member alignment