2010-10-22 12 views
33

He dos idénticos (pero con nombres diferentes) estructuras C:casting una estructura de C en otro

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


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

Ahora quiero asignar una variable a una variable CMAcceleration Vector3D (copiar toda la estructura). ¿Cómo puedo hacer esto?

He intentado lo siguiente, pero conseguir estos errores de compilación:

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

Por supuesto que se puede recurrir para establecer todos los miembros de forma individual:

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

pero que parece bastante incómodo.

¿Cuál es la mejor solución?

+1

¿No puedes typedef (por ejemplo typedef struct CMAcceleration Vector3D)? Ooops, alguien ya había señalado ... – Nyan

Respuesta

37

Esa es su única solución (aparte de envolverlo en una función):

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

en realidad se podría echarlo, como esto (usando punteros)

Vector3d *vector = (Vector3d*) &acceleration; 

pero esto no está en las especificaciones y, por lo tanto, el comportamiento depende del compilador, el tiempo de ejecución y el gran monstruo del espacio verde.

+1

+1: Buena respuesta. Describe tanto el único método que garantiza que funciona, como el método que generalmente funcionará en la práctica, y la razón por la cual este método no está definido técnicamente. –

+1

+1 Solo agregaría que la técnica de lanzamiento es bastante común, no es realmente malvada. –

+1

+1 para envolverlo en una función. Incluso algo tan trivial como esto vale la pena hacer una subrutina para. – alesplin

15

Puede usar un puntero para hacer el encasillado;

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

+1 Esta es la solución obvia :) – Venemo

+4

Debe señalarse que el compilador no está obligado a garantizar que ambas estructuras estén empaquetadas y alineadas de la misma manera. –

+9

Este comportamiento no está definido debido a un aliasing estricto. http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html – Secure

4

utiliza una función de utilidad para que:

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));

Tenga en cuenta que esto sólo funciona si la disposición física de las estructuras de la memoria son idénticos. Sin embargo, como señaló @Oli, ¡el compilador no está obligado a garantizar esto!

+1

Debe señalarse que el compilador no está obligado a garantizar que ambas estructuras estén empaquetadas y alineadas de la misma manera. –

+0

@Oli Charlesworth: tienes razón y actualicé la respuesta en consecuencia – groovingandi

+0

@OliverCharlesworth: se necesitaría un compilador patológicamente perverso para romper esta suposición, esp. considerando la respuesta aceptada a esta pregunta: https://stackoverflow.com/questions/19804655/arec-c-structs-with-the-same-members-types-guaranteed-to-have-the-same-layout-in – chqrlie

5

Why dont you use.

typedef CMAcceleration Vector3d; 

(en lugar de crear una estructura totalmente nueva)

en ese caso vector = acceleration; compila bien.

+0

Obtengo un 'warning: 'typedef struct Vector3d Vector3d' no se refiere al tipo no calificado, por lo que no se usa para el enlace'. También en este caso, 'CMAcceleration' está en un marco débilmente vinculado, por lo que me abstengo de usarlo en mi archivo .h. –

+3

Si la estructura 'CMAcceleration' proviene de un marco separado, se recomienda hacer la copia de campo por campo, en lugar de los trucos de memcpy o de tipo de escritura, para robustecer su código en el caso de un futuro cambios en el otro marco. (Incluso si sabes que los diseños de las estructuras son idénticos hoy, quizás no se mantendrán de esa manera en versiones posteriores). –

1

Una manera segura (aunque algo complicado) para hacerlo sería el uso de un sindicato:

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

Los valores son reinterpretados (desde C99) cuando el miembro visitada no es el último set. En este caso, establecemos la aceleración y luego accedemos al vector, por lo que la aceleración se reinterpreta.

Así se implementa la función NSRectToCGRect, por ejemplo.

1

Esto se logra fácilmente a través de una unión :

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); 
}