Home > Software engineering >  Cast struct to its first member inside a function(void *)
Cast struct to its first member inside a function(void *)

Time:09-15

I have an opaque structure like this:

// base.h
typedef struct Base Base;
Base *create_base(void);
void base_set_first(Base *, int);

// base.c
#include "base.h"

struct Base {
    int first;
};

Base *create_base(void)
{
    Base *new = malloc(sizeof(Base));
    return new;
}

void base_set_first(Base *base, int value)
{
    base->first = value;
}

The first member of another opaque structure is a pointer to Base:

// derived.h
typedef struct Derived Derived;
Derived *create_derived(void);

// derived.c
#include "derived.h"
#include "base.h"

struct Derived {
    Base *base;
};

Derived *create_derived(void)
{
    Derived *new = malloc(sizeof(Derived));
    new->base = create_base();
    return new;
}

Is it 'legal' to pass a structure Derived to a function that accepts a void * and then cast it to its first member?

void function(void *ptr)
{
    Base *base = (Base *)ptr; // <-- it seems to work well but is this 'legal' or UB?
    base_set_first(base, 1);
    //...
}

Derived *d = create_derived();
function(d);

CodePudding user response:

It's valid to cast a structure pointer to a pointer to the type of its first member. The specification says:

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.

But that's not what you're doing. Your code in function() would be correct if the declaration were:

struct Derived {
    Base base;
};

But in your actual code, Derived.base is a Base* pointer, not a Base, so you need to add a level of indirection.

void function(void *ptr)
{
    Base **base = (Base **)ptr;
    base_set_first(*base, 1);
    //...
}

CodePudding user response:

From the C Standard (6.3.2.3 Pointers)

1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

and (6.7.2.1 Structure and union specifiers)

15 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.

  • Related