2011-09-10 15 views
10
typedef struct foo_s { 
    int a; 
} foo; 

typedef struct bar_s { 
    foo; 
    int b; 
} bar; 

In sostanza voglio fare:Posso "estendere" una struct in C?

bar b; 
b.a; 

So che avrei potuto fare b.foo_name.a se avessi chiamato lo struct foo in bar, ma Id preferisco non farlo.

Un modo per farlo?

Questa domanda ha ottenuto una varietà di risposte diverse, quindi lasciatemi spiegare la necessità. La ragione per cui voglio farlo è perché ho una libreria che devo adattare alla mia situazione, il che significa che non posso modificare la decodifica della struct originale. Inoltre, tutto ciò che devo fare è aggiungere 1 elemento all'inizio della struct (perché all'inizio? Perché ho una struttura 'object' che dirige tutte le strutture nel progetto). Potrei semplicemente incorporare la struttura come dici tu, ma è DAVVERO fastidiosa dato che tutti i riferimenti dovranno essere digitati 'variable-> image.location' che 'immagine'. digitato un miliardo di tipi è davvero fastidioso.

+0

Alcuni compilatori offrono esattamente questo comportamento come un'estensione Personalmente ritengo che offra troppi rischi (annidando la struttura di sei strati in profondità e dimenticando i nomi che hai usato), quindi mi limito a nominare il primo membro '_' - corto, coerente, fuori mano e non terribilmente inopportuno . (Il nome potrebbe essere effettivamente riservato dallo standard e/o dal compilatore ma dubito che qualcuno lo userebbe se lo fosse.) –

+0

Se 'image' è noioso da digitare molto, usa' img'. Usa find-and-replace per farlo per te. Non preoccuparti: qualunque cosa tu debba fare, sopravvivrai. –

+0

@ chacham15 Amico, evita di codificare una domanda in C come C++. Questi due sono lingue completamente diverse. –

risposta

19

Evidentemente this feature has been added to C11, ma purtroppo non ho accesso a un compilatore C di recente epoca (> = GCC 4.6.2).

typedef struct foo { 
    int a; 
} foo; 

typedef struct bar { 
    struct foo; 
    int b; 
} bar; 

int main() { 
    bar b; 
    b.a = 42; 
    b.b = 99; 
    return 0; 
} 
+1

Nota che questo funziona perché all'interno di 'bar',' struct foo' è usato in modo anonimo (cioè senza nome di variabile). –

7

Non possibile in C come hai fatto tu. Ma puoi simulare l'ereditarietà con una variabile membro foo in bar.

typedef struct bar_s { 
    foo obj; 
    int b; 
} bar; 

bar b; 
b.obj.a = 10; 
+5

Questo si chiama [composizione] (http://en.wikipedia.org/wiki/Composition_over_inheritance) se non sbaglio. – michaelsnowden

1

Puoi provare a utilizzare l'ereditarietà:

struct foo_s 
{ 
    int a; 
}; 

struct bar_s: foo_a 
{ 
    int b; 
}; 

Lavori in C++, non è sicuro se funziona in C.

+1

mio male, hai ragione, non so perché ho aggiunto il tag C++, intendevo c. sry! – chacham15

+4

Sono sicuro che non è C –

+0

La migliore risposta, anche se è C++. – lama12345

2

Se Ment

typedef struct foo_s { 
    int a; 
    } foo; 

typedef struct bar_s { 
foo my_foo; 
int b; 
} bar; 

modo da poter fare :

bar b; b.my_foo.a = 3; 

Altrimenti, Non c'è modo di farlo in C poiché lo sizeof(bar_s) è un danno per il tempo di compilazione. Non è una buona pratica, ma puoi salvare un puntatore void * ptr; all'interno di bar_s e un'altra enum che descrive il tipo ptr e eseguire il cast in base al tipo.

cioè:

typedef enum internalType{ 
    INTERNAL_TYPE_FOO = 0, 
}internalType_t; 

typedef struct bar_s { 
internalType_t ptrType; 
void* ptr; 
int b; 
} bar; 

e poi:

bar b; foo f; 
b.ptrType = INTERNAL_TYPE_FOO; 
b.ptr = &f; 

e qualche altro posto nel codice:

if (b.ptrType == INTERNAL_TYPE_FOO) { 
    foo* myFooPtr = (foo *)b.ptr; 
} 
+0

La domanda non ha fatto cenno di volere "tipi" di runtime come quelli che descrivi. –

+0

@Chris Lutz Forse fuori bordo, ma risponde alla domanda ... –

+0

@Chris, potresti avere ragione, ma il "bar.a" mi sembrava un codice python (dove le classi possono essere estese in fase di esecuzione) . Quindi questo è ciò che mi è venuto in mente ... –

9

È possibile, utilizzando i puntatori, perché un puntatore ad una struttura l'oggetto è garantito per puntare il suo primo membro. Vedi per es. this article.

#include <stdlib.h> 
#include <stdio.h> 

typedef struct foo_s { 
    int a; 
} foo; 

typedef struct bar_s { 
    foo super; 
    int b; 
} bar; 

int fooGetA(foo *x) { 
    return x->a; 
} 

void fooSetA(foo *x, int a) { 
    x->a = a; 
} 

int main() { 
    bar* derived = (bar*) calloc(1, sizeof(bar)); 
    fooSetA((foo*) derived, 5); 
    derived->b = 3; 
    printf("result: %d\n", fooGetA((foo*) derived)); 
    return 0; 
} 
+1

È ancora meglio quando prendono i puntatori "void *" ed evitano il casting sul lato del chiamante. (I valori di "migliori" di alcune persone potrebbero essere diversi dai miei). –

+1

Sì, è sicuramente più conveniente per il chiamante. Volevo dimostrare che l'autore delle funzioni foo non deve sapere che la struttura "reale" è qualcosa di diverso da foo. –

+1

Bene, entrambi gli approcci sono giusto e sbagliato allo stesso tempo. Il dover typecast impone che il codice client sia di bassa qualità. Questo può essere risolto usando 'void *', tuttavia ciò elimina la sicurezza del tipo che è anche peggio. Ancora queste sono una bella risposta e alcuni buoni argomenti, specialmente l'ultimo commento di Jouni quando si tratta di astrazioni (il punto di ereditarietà), tuttavia i fattori di cui sopra devono essere presi in considerazione. Ecco la soluzione C11: ** http: //modelingwithdata.org/arch/00000113.htm**; Mi chiedo se ci fosse una soluzione alternativa per altre versioni di C – Powerslave

0

Può essere facilmente fatto tramite preprocessore:

Creare un file denominato base_foo.h:

int foo; 

Poi basta includerlo:

typedef struct foo_s { 
    #include "base_foo.h" 
} foo; 

typedef struct bar_s { 
    #include "base_foo.h" 
    int b; 
} bar;