10

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?

  • I always see people make unions of the two and use that. That way you always get the strictest version for both. – xaxxon Jun 09 '13 at 06:47
  • If it compiles, it doesn't violate standards. No? – basin Jun 09 '13 at 10:41
  • 3
    @basin - Not true, and in any case technically aliasing requirements pertain to accesses through pointers, thus can only be violated at runtime (though a clever enough compiler _might_ catch _some_ such problems during compilation.) – jaymmer - Reinstate Monica Jun 09 '13 at 10:52

2 Answers2

3

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)

Joe
  • 6,497
  • 4
  • 29
  • 55
  • So does the second quote mean to say that if I access a member `a` through a struct pointer, it is valid as long as the struct pointer aliases a location compatible with _any_ of the struct's members even if `a` is an incompatible type? – jaymmer - Reinstate Monica Jun 10 '13 at 03:58
  • No. To my understanding, it really only states that if you write to a struct pointer, you can affect the storage pointed to by any pointer to any of the member types. However the reverse is not implied, so you can affect any uint32 member via an uint32 pointer, but not any of the others. Due to the first quote, I believe there is an exception if your struct has an uint32 as a first member. – Joe Jun 10 '13 at 08:14
-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
basin
  • 3,949
  • 2
  • 27
  • 63
  • I don't think that's true at all. Under what circumstances _should_ I care about aliasing, if not in any of the examples? – jaymmer - Reinstate Monica Jun 09 '13 at 10:56
  • I tried your examples. gcc doesn't warn/no unexpected results. Even the sample from the URL you posted works correctly with gcc4. gcc3 DOES have an issue with the example from the URL, but not your examples. I think, because they are function arguments, not local variables – basin Jun 09 '13 at 11:30
  • 1
    what optimisation/compiler flags did you use? gcc doesn't catch all aliasing issues (no compiler does), and I wouldn't be surprised if such simple examples as mine worked in practice in spite of violating the standard (as far as I can tell.) However, I absolutely do not want to rely on the observation that the above works on any one compiler. – jaymmer - Reinstate Monica Jun 09 '13 at 12:49
  • @jaymmer: Until it became fashionable for compilers to avoid doing anything not mandated by the Standard, the act of casting an int* to a Frizzly* would guard against aliasing problems in cases where writes of one type preceded the cast and reads of the other type followed it. One would need to worry about aliasing in cases where compilers had no reason to expect it, but not in reasons where it would be obvious. Unfortunately, popular compilers like gcc and clang have been taken over by people who don't care about whether a language is usable. – supercat May 17 '16 at 05:27
  • *None of your examples break the rules* Completely, utterly, and in all ways just plain wrong. In the interests of anyone stumbling upon this blatantly wrong answer in the future, accessing a `char` array as anything other than a `char` array violates both [strict aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) ... – Andrew Henle Nov 05 '19 at 19:57
  • (cont) and potentially [**6.3.2.3 Pointers**, p7](https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7): "A pointer to an object type may be converted to a pointer to a different object type. **If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined**." Just Google ["`SIGBUS` ARM"](https://www.google.com/search?q=SIGBUS+arm). – Andrew Henle Nov 05 '19 at 20:00
  • For example, [**Structure assignment in Linux fails in ARM but succeeds in x86**](https://stackoverflow.com/questions/19114491/structure-assignment-in-linux-fails-in-arm-but-succeeds-in-x86) – Andrew Henle Nov 05 '19 at 20:01