2009-05-20 17 views
28

¿Cuál es la mejor manera de resolver la siguiente dependencia circular en la definición de estas estructuras?
Nota la etiqueta de idioma C - Estoy buscando una solución en gcc estándar C.¿Resuelve la dependencia de typedef circular?

typedef struct { 
    char* name; 
    int age; 
    int lefthanded; 
    People* friends; 
} Person; 

typedef struct { 
    int count; 
    int max; 
    Person* data; 
} People; 

Respuesta

27

Forward-declarar una de las estructuras:


struct people; 

typedef struct { 
    /* same as before */ 
    struct people* friends; 
} Person; 

typedef struct people { 
    /* same as before */ 
} People; 
+2

Alguien debería mencionar que tendría que escribir typedef struct people {....} Personas; entonces. por lo tanto, no es exactamente lo mismo que antes (de todos modos, es una buena idea dar nombres de etiquetas explícitos) –

+0

Tienes razón, era perezoso volver atrás y editar la respuesta, lo haré ahora. –

+1

En struct Persona no podemos declarar 'struct people friends' en lugar de un puntero ?. Me arroja un error. Ej: 'struct people;' 'typedef struct {struct people friends;} Person;' y 'typedef struct people {Person person;};' –

1

Desde Person sólo quiere un puntero a People, que debería estar bien para simplemente predeclarar este último:

typedef struct People People; 

a continuación, cambie la segunda declaración que acaba de declarar el uso de la etiqueta de estructura, así:

struct People { 
    int count; 
    int max; 
    Person data[]; 
}; 
0
struct People_struct; 

typedef struct { 
    char* name; 
    int age; 
    int lefthanded; 
    struct People_struct* friends; 
} Person; 

typedef struct People_struct { 
    int count; 
    int max; 
    Person data[]; 
} People; 
1
struct _People; 

typedef struct { 
    char* name; 
    int age; 
    int lefthanded; 
    struct _People* friends; 
} Person; 

struct _People { 
    int count; 
    int max; 
    Person data[1]; 
}; 

Nota: Es Person data[]; estándar?

+0

- He actualizado el ejemplo;) –

5

En cuanto a la legibilidad:

typedef struct Foo_ Foo; 
typedef struct Bar_ Bar; 

struct Foo_ { 
    Bar *bar; 
}; 

struct Bar_ { 
    Foo *foo; 
}; 

Podría ser una buena idea para evitar typedef struct por completo;

35

La respuesta está en la diferencia entre declaración y definición. Está intentando declarar y definir en el mismo paso (en el caso de un nuevo tipo mediante typedef). Debe dividirlos en diferentes pasos para que el compilador sepa de qué está hablando con antelación.

typedef struct Person Person; 
typedef struct People People; 

struct Person { 
    char* name; 
    int age; 
    int lefthanded; 
    People* friends; 
}; 

struct People { 
    int count; 
    int max; 
    Person* data; 
}; 

Tenga en cuenta la adición de los dos 'typedefs' vacíos en la parte superior (declaraciones). Esto le dice al compilador que el nuevo tipo Persona es del tipo 'struct Person' para que cuando vea que dentro de la definición de struct People sepa lo que significa.

En su caso particular, podría salirse con la suya únicamente antes de predecir el People typdef porque ese es el único tipo utilizado antes de que se defina. Para cuando ingresas a la definición de struct People, ya has definido completamente el tipo Persona. Así que el siguiente también funcionaría, pero se NO RECOMENDADO porque es frágil:

typedef struct People People; 

typedef struct { 
    char* name; 
    int age; 
    int lefthanded; 
    People* friends; 
} Person; 

struct People { 
    int count; 
    int max; 
    Person* data; 
}; 

Si se cambia el orden de las definiciones de la estructura (struct en movimiento por encima de la gente typedef de la persona) que vuelva a fallar. Eso es lo que hace que esto sea frágil y, por lo tanto, no recomendado.

Tenga en cuenta que este truco NO funciona si incluye una estructura del tipo especificado en lugar de un puntero al mismo. Así, por ejemplo, la siguiente no va a compilar:

typedef struct Bar Bar; 

struct Foo 
{ 
    Bar bar; 
}; 

struct Bar 
{ 
    int i; 
}; 

El código anterior da un error de compilación porque el tipo de barra es incompleta cuando se trata de usarlo dentro de la definición de Foo estructura. En otras palabras, no sabe cuánto espacio asignar a la barra de miembros de la estructura porque no ha visto la definición de barra de estructuras en ese punto.

Este código compilará:

typedef struct Foo Foo; 
typedef struct Bar Bar; 
typedef struct FooBar FooBar; 

struct Foo 
{ 
    Bar *bar; 
}; 

struct Bar 
{ 
    Foo *foo; 
}; 

struct FooBar 
{ 
    Foo  foo; 
    Bar  bar; 
    FooBar *foobar; 
}; 

Esto funciona, incluso con los punteros circulares dentro de Foo y Bar, porque 'Foo' los tipos y 'Bar' han sido declarados previamente (pero que aún no definido) para que el compilador pueda construir un puntero a ellos.

En el momento en que empezamos a definir FooBar, hemos definido cuán grandes son tanto Foo como Bar para poder incluir los objetos reales allí. También podemos incluir un puntero autorreferencial para escribir FooBar porque hemos pre-declarado el tipo.

Tenga en cuenta que si movió la definición de struct FooBar por encima de las definiciones de struct Foo o Bar, no compilaría por el mismo motivo que el ejemplo anterior (tipo incompleto).

+0

Buena respuesta. Bienvenido a SO! –

+0

+1 por la buena explicación! – Dogbert

Cuestiones relacionadas