cstructundefined-behaviormemory-alignment

Is it safe to cast a struct pointer to a different struct pointer having a prefix of elements?


I have this struct:

typedef struct {
    int *array;
    int first;
    int last;
} arguments;

and I would be interested in having a different struct with some added arguments:

typedef struct {
    int *array;
    int first;
    int last;
    // only used in children
    int read_fd;
    int write_fd;
} child_arguments;

but I have some functions that expect arguments

void do_something(arguments* args) { ... }

Is it safe to do this kind of cast:

void do_something_in_child(child_arguments* child_args) {
    do_something( (arguments*) child_args );
}

I imagine that since C doesn't reorder members in a struct, and elements added afterwards shouldn't affect the padding of old elements, this might be safe. But is it? And if it is, is it C compliant or is it technically undefined behavior but safe in most compilers?


Solution

  • @dbush posted an answer of one way to solve it through the common initial sequence rule and unions.

    Another way, which is perhaps more rugged and reliable in terms of compiler support, is to have one struct declare an instance of the other struct as its first member. As it turns out, that's the recommended way of implementing inheritance & polymorphism in C, which seems to be what you are doing.

    There's this rule found in the current C23 standard below 6.7.3.2:

    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.

    This allows us to convert freely from a pointer to a struct and a pointer to the first item in the struct. This rule goes way back to the first C standard and compilers have supported it from a time before strict aliasing debacles became mainstream in the early 2000s.

    (The only minor issue is that C actually doesn't specify "suitably converted", although that's a quality of implementation issue and every compiler I ever used interprets it as "explicitly cast to the corresponding pointer type".)

    Check out this Q&A for some examples of how to do that: How do you implement polymorphism in C?