Em C, um ponteiro para uma estrutura sempre aponta para seu primeiro membro?

Suponha que eu tenha um número de estruturas C para as quais gostaria que um determinado conjunto de funções operasse.

Eu estou querendo saber se o seguinte é uma abordagem legítima:

typedef struct Base { int exampleMember; // ... } Base; typedef struct Foo { Base base; // ... } Foo; typedef struct Bar { Base base; // ... } Bar; void MethodOperatesOnBase(void *); void MethodOperatesOnBase(void * obj) { Base * base = obj; base->exampleMember++; } 

No exemplo, você notará que ambas as structs Foo e Bar começam com um membro Base .

E, que em MethodOperatesOnBase , eu casting o parâmetro void * para Base * .

Eu gostaria de passar pointers para Bar e pointers para Foo para este método e contar com o primeiro membro da estrutura para ser uma estrutura Base .

Isso é aceitável ou existem alguns problemas (possivelmente específicos do compilador) dos quais preciso estar ciente? (Como algum tipo de esquema de embalagem / preenchimento que mudaria a localização do primeiro membro de uma estrutura?)

Sim, o padrão C garante especificamente que isso funcionará.

(C1x §6.7.2.1.13: “Um ponteiro para um object de estrutura, adequadamente convertido, aponta para seu membro inicial … e vice-versa. Pode haver preenchimento sem nome dentro de um object de estrutura, mas não em seu início.”)

Não estou discordando de nenhuma das respostas dizendo que o que você sugeriu funcionará, mas no interesse de uma discussão mais completa (sem sugerir o uso de C ++!), Por que não fazer algo como

 typedef struct Base ... /* The types defined exactly as before */ typedef struct Foo ... typedef struct Bar ... /* The function takes a Base* since that is what it actually works with*/ void MethodOperatesOnBase(Base* pbase) { /* Do something... */ } /* Now call it like this: */ Foo foo; Bar bar; MethodOperatesOnBase(&foo.base); MethodOperatesOnBase(&bar.base); 

Existe alguma razão que não funcione e você precisa usar void * ? Eu não vejo que isso é muito mais trabalho e tem a vantagem de segurança de tipo.

O gtk + inteiro é implementado assim. Não consigo pensar em um exemplo melhor. Dê uma olhada em http://git.gnome.org/browse/gtk+/tree/gtk/