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.