2012-01-02 4 views
7

Supponiamo di avere due struct:Struct puntatore compatibilità

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

E 'sicuro di gettare Struct2 *-Struct1 *? Cosa dice la specifica ANSI a riguardo? So che alcuni compilatori hanno la possibilità di riordinare i campi delle strutture per ottimizzare l'utilizzo della memoria, che potrebbe rendere le due strutture non compatibili. C'è un modo per essere sicuro che questo codice sarà valido, indipendentemente dal flag del compilatore?

Grazie!

+2

* Riordino * i membri non sono consentiti dalla norma AFAIK. Credo che l'inserimento di diverse quantità di imbottitura sarebbe comunque consentito. – delnan

+0

@delnan Oh, quindi quella struct 'packing' disabiliterà solo l'allineamento? Grazie, non lo sapevo! – Waneck

risposta

5

struct tipi puntatori hanno sempre la stessa rappresentazione in C.

(C99, 6.2.5p27) "Tutti i puntatori per strutturare tipi avranno lo stesso esigenze di rappresentazione e allineamento come reciprocamente."

e membri in tipi di strutture sono sempre in ordine in C.

(C99, 6.7.2.1p5) una" struttura è un tipo costituito da una sequenza di membri, la cui memorizzazione è allocato in una sequenza ordinata "

+1

Questo non risponde alla domanda; anche con questi vincoli potrebbe ancora essere una violazione di aliasing. Tuttavia, in determinate condizioni, lo standard C consente esplicitamente ciò che l'OP vuole. –

+0

Grazie mille per queste citazioni dalla specifica ANSI. Questo per me rende chiaro che questo è sicuro! – Waneck

+0

@R. Quali condizioni? – Waneck

2

Probabilmente funzionerà. Ma sei molto corretto nel chiedere come puoi essere sicuro che questo codice sarà valido. Quindi: da qualche parte nel tuo programma (all'avvio forse) incorpora un gruppo di istruzioni ASSERT che assicurano che offsetof(Struct1.a_short) sia uguale a offsetof(Struct2.a_short) ecc. Inoltre, alcuni programmatori diversi da quello che potresti modificare un giorno una di queste strutture ma non l'altra, quindi meglio sicuro che dispiaciuto.

+0

L'utilizzo di asserzioni statiche sarebbe molto meglio ... –

+0

Grazie Mike, aggiungo di sicuro alcune affermazioni per garantire questo! – Waneck

+0

@R .. statico asserisce? Non sapevo che esistessero.[L'ho cercato e ho scoperto] (http://stackoverflow.com/questions/3385515/static-assert-in-c). Hai ragione, grazie. –

3

È sicuro, per quanto ne so.

Ma è molto meglio, se possibile, di fare:

typedef struct { 
    Struct1 struct1; 
    short another_short; 
} Struct2; 

allora hai anche detto al compilatore che Struct2 inizia con un'istanza di Struct1, e dal momento che un puntatore ad una struct punta sempre al suo primo membro, sei sicuro di trattare un Struct2 * come Struct1 *.

+0

beh, se c'è la minima possibilità che un giorno 'offsetof (Struct1.a_short)' sarà trovato a NON essere uguale a 'offsetof (Struct2.a_short)' allora c'è una uguale quantità di possibilità che il giorno in cui 'offsetof (Struct2.struct1)' non sia trovato uguale a zero. (Il che significherebbe '& struct2! = (Struct2 *) & struct2.struct1'). –

+0

In effetti, questo modo è molto meglio! :) Grazie! – Waneck

+0

Se struct1 e struct2 hanno entrambi impostato "int" per primo e "int" per l'allineamento a 32 bit, entrambi i tipi di struttura potrebbero contenere 8 byte, ma la forma alternativa di Struct2 richiederebbe 12 byte. Se i compilatori rispettano la regola della sequenza iniziale comune, la forma deve essere valida (e la forma a 8 byte sarebbe più efficiente), ma anche quando invocata in modalità C89, gcc non mantiene più le garanzie di C89 tranne quando '-fno-strict- la bandiera di aliasing' è usata. – supercat

1

Sì, è giusto farlo!

Un programma di esempio è il seguente.

#include <stdio.h> 

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

int main(void) 
{ 

    Struct2 s2 = {1, 2, 3}; 
    Struct1 *ptr = &s2; 
    void *vp = &s2; 
    Struct1 *s1ptr = (Struct1 *)vp; 

    printf("%d, %d \n", ptr->a_short, ptr->id); 
    printf("%d, %d \n", s1ptr->a_short, s1ptr->id); 

    return 0; 
}