2010-10-22 20 views
33

ho due identici (ma con nomi diversi) strutture C:Casting una struttura C in un altro

typedef struct { 
     double x; 
     double y; 
     double z; 
} CMAcceleration; 


typedef struct { 
    double x; 
    double y; 
    double z; 
} Vector3d; 

Ora voglio assegnare una variabile CMAcceleration ad una variabile Vector3D (copiare l'intero struct). Come posso fare questo?

Ho provato quanto segue ma ottenere questi errori di compilazione:

vector = acceleration;   // "incompatible type" 
vector = (Vector3d)acceleration; // "conversion to non-scalar type requested" 

Certo che posso ricorrere a impostare singolarmente tutti i membri:

vector.x = acceleration.x; 
vector.y = acceleration.y; 
vector.z = acceleration.z; 

ma che sembra piuttosto scomodo.

Qual è la soluzione migliore?

+1

non puoi semplicemente typedef (diciamo typedef struct CMAcceleration Vector3D)? Ooops, qualcuno aveva già sottolineato ... – Nyan

risposta

37

Questo è l'unica soluzione (a parte avvolgendolo in una funzione):

vector.x = acceleration.x; 
vector.y = acceleration.y; 
vector.z = acceleration.z; 

si potrebbe effettivamente cast, come questo (usando i puntatori)

Vector3d *vector = (Vector3d*) &acceleration; 

ma questo non è nelle specifiche e quindi il comportamento dipende dal compilatore, dal runtime e dal grande mostro dello spazio verde.

+1

+1: buona risposta. Descrive sia l'unico metodo che è garantito che funzioni, sia il metodo che di solito funziona nella pratica, e il motivo per cui questo metodo è tecnicamente indefinito. –

+1

+1 Vorrei solo aggiungere che la tecnica di casting è abbastanza comune - non è come se fosse veramente malvagia. –

+1

+1 per avvolgerlo in una funzione. Anche qualcosa di così banale come questo vale la pena fare una subroutine per. – alesplin

15

È possibile utilizzare un puntatore per eseguire il typecast;

vector = *((Vector3d *) &acceleration); 
+1

+1 Questa è la soluzione ovvia :) – Venemo

+4

Va sottolineato che il compilatore non è obbligato a garantire che entrambe le strutture siano imballate e allineate allo stesso modo. –

+9

Questo comportamento non è definito a causa del rigoroso aliasing. http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html – Secure

4

si utilizza una funzione di utilità per questo:

void AccelerationToVector(struct CMAcceleration* from, struct Vector3d* to) 
{ 
    to->x = from->x; 
    to->y = from ->y; 
    to->z = from->z; 
} 
4

memcpy(&vector, &acceleration, sizeof(Vector3d));

Si prega di notare che questo funziona solo se la disposizione fisica delle struct in memoria sono identici. Tuttavia, come ha sottolineato @Oli, il compilatore non è obbligato a garantire questo!

+1

Va sottolineato che il compilatore non è obbligato a garantire che entrambe le strutture siano imballate e allineate allo stesso modo. –

+0

@Oli Charlesworth: hai ragione e ho aggiornato la risposta di conseguenza – groovingandi

+0

@OliverCharlesworth: ci vorrebbe un compilatore patologicamente perverso per rompere questa ipotesi, esp. considerando la risposta accettata a questa domanda: https://stackoverflow.com/questions/19804655/are-c-struct-with-the-same-members-types-guaranteed-to-have-the-same-layout-in – chqrlie

5

Perché non lo uso.

typedef CMAcceleration Vector3d; 

(invece di creare una nuova struttura)

in questo caso vector = acceleration; compila bene.

+0

Ricevo un avvertimento: 'typedef struct Vector3d Vector3d' non si riferisce al tipo non qualificato, quindi non viene usato per il collegamento'. Anche in questo caso, 'CMAcceleration' si trova in un framework debolmente collegato, quindi mi astengo dall'utilizzarlo nel mio file .h. –

+3

Se la struttura 'CMAcceleration' proviene da un framework separato, si consiglia di eseguire la copia campo per campo, anziché i memcpy o i trucchi di punteggiatura, per rendere il codice robusto in caso di futuro cambiamenti nell'altro quadro. (Anche se sai che i layout della struct sono identici oggi, forse non rimarranno così nelle versioni successive.) –

1

Un modo sicuro (anche se un po 'contorto) per farlo sarebbe quello di utilizzare un sindacato:

union { CMAcceleration a, Vector3d v } tmp = { .a = acceleration }; 
vector = tmp.v; 

valori vengono reinterpretati (dal C99) quando l'organo si accede non è l'ultimo set uno. In questo caso, impostiamo l'accelerazione e quindi accediamo al vettore, quindi l'accelerazione viene reinterpretata.

Questo è il modo in cui è implementata la funzione NSRectToCGRect, ad esempio.

1

Questo è facilmente ottenibile attraverso un unione:

typedef struct { 
     double x; 
     double y; 
     double z; 
} CMAcceleration; 

typedef struct { 
    double x; 
    double y; 
    double z; 
} Vector3d; 

typedef union { 
    CMAcceleration acceleration; 
    Vector3d vector; 
} view; 

int main() { 
    view v = (view) (Vector3d) {1.0, 2.0, 3.0}; 
    CMAcceleration accel = v.acceleration; 
    printf("proof: %g %g %g\n", accel.x, accel.y, accel.z); 
}