Please consider the following code:
typedef struct {
int type;
} object_t;
typedef struct {
object_t object;
int age;
} person_t;
int age(object_t *object) {
if (object->type == PERSON) {
return ((person_t *)object)->age;
} else {
return 0;
}
}
Is this legal code or is it violating the C99 strict aliasing rule? Please explain why it is legal/illegal.
The strict aliasing rule limits by what types you access an object (a region of memory). There are a few places in the code where the rule might crop up: within
age()
and when callingage()
.Within
age
, you haveobject
to consider.((person_t *)object)
is an lvalue expression because it has an object type and it designates an object (a region of memory). However, the branch is only reached ifobject->type == PERSON
, so (presumably) the effective type of the object is aperson_t*
, hence the cast doesn't violate strict aliasing. In particular, strict aliasing allows:When calling
age()
, you will presumably be passing anobject_t*
or a type that descends fromobject_t
: a struct that has anobject_t
as the first member. This is allowed as:Furthermore, the point of strict aliasing is to allow for optimizing away loading values into registers. If an object is mutated via one pointer, anything pointed to by pointers of an incompatible type are assumed to remain unchanged, and thus don't need to be reloaded. The code doesn't modify anything, so shouldn't be affected by the optimization.
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
As an add-on to the accepted answer, here is the full citation from the standard, with the important part highlighted that the other answer omitted, and one more:
I find your example to be a perfect place for a void pointer:
Why? Because your obvious intention is to give different "object" types to such a function, and it gets the information according to the encoded type. In your version, you need a cast each time you call the function:
age((object_t*)person);
. The compiler will not complain when you give the wrong pointer to it, so there is no type safety involved, anyway. Then you can as well use a void pointer and avoid the cast when calling the function.Alternatively you could call the function with
age(&person->object)
, of course. Each time you call it.One acceptable way that is explicitly condoned by the standard is to make a union of structs with identical initial segment, like so:
Now you can pass an
object_t * p
and examinep->tag.value
with impunity, and then access the desired union member.Strict aliasing rule is about two pointers of different types referencing the same location in memory (ISO/IEC9899/TC2). Although your example reinterprets the address of
object_t object
as an address ofperson_t
, it does not reference memory location insideobject_t
through the reinterpreted pointer, becauseage
is located past the boundary ofobject_t
. Since memory locations referenced through pointers are not the same, I'd say that it is not in violation of the strict aliasing rule. FWIW,gcc -fstrict-aliasing -Wstrict-aliasing=2 -O3 -std=c99
seems to agree with that assessment, and does not produce a warning.This is not enough to decide that it's legal code, though: your example makes an assumption that the address of a nested structure is the same as that of its outer structure. Incidentally, this is a safe assumption to make according to the C99 standard:
The two considerations above make me think that your code is legal.