Convertir una estructura C en otra

6 minutos de lectura

Tengo dos estructuras C idénticas (pero con nombres diferentes):

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


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

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

Intenté lo siguiente pero obtuve estos errores del compilador:

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

Por supuesto, puedo recurrir a establecer todos los miembros individualmente:

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

pero eso parece bastante inconveniente.

¿Cuál es la mejor solución?

  • ¿No puedes simplemente typedef (por ejemplo, typedef struct CMAcceleration Vector3d)? Vaya, alguien ya había señalado…

    – Nyan

    22 de octubre de 2010 a las 11:37


avatar de usuario
Georg Scholly

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;

De hecho, podría lanzarlo, así (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: Buena respuesta. Describe tanto el único método que se 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á técnicamente definido.

    –Oliver Charlesworth

    22 de octubre de 2010 a las 11:18

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

    – Profesor Falken

    22 de octubre de 2010 a las 11:41

  • +1 por envolverlo en una función. Incluso algo tan trivial como esto bien vale la pena hacer una subrutina.

    – alesplín

    22 de octubre de 2010 a las 15:43

  • ¿Qué sucede si declaramos CMAcceleration como struct { Vector3d vec; };? Luego, las instancias de CMAcceleration tendrán Vector3d en primer lugar. sizeof(Vector3d) bytes ¿Eliminaría eso el alias estricto al realizar la conversión de punteros?

    – holgac

    11 de abril de 2015 a las 9:03

  • Entonces ya no necesitaríamos lanzar punteros. Podríamos simplemente asignar directamente vector = acc.vec;.

    – Hermann Doppes

    09/01/2016 a las 10:40

Podría usar un puntero para hacer el encasillado;

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

  • Cabe señalar que el compilador no está obligado a asegurarse de que ambas estructuras estén empaquetadas y alineadas de la misma manera.

    –Oliver Charlesworth

    22 de octubre de 2010 a las 11:10

  • Este es un comportamiento indefinido debido al alias estricto. cellperformance.beyond3d.com/articles/2006/06/…

    – Seguro

    22 de octubre de 2010 a las 11:12

  • @Secure Es una pena porque me gustaría usar esta técnica para no copiar, sino en realidad alias (cambiar el tipo de) una estructura.

    – Miguel

    22 de octubre de 2014 a las 2:40

  • @Michael: Especifique que al usar gcc o clang, el código debe compilarse con el -fno-strict-aliasing bandera (otros compiladores pueden usar una bandera con el mismo nombre, o pueden ser menos agresivos en sus optimizaciones de alias que gcc o clang).

    – Super gato

    12 de septiembre de 2017 a las 14:58

avatar de usuario
ranurandoandi

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

Tenga en cuenta que esto solo funciona si el diseño físico de las estructuras en la memoria es idéntico. Sin embargo, como señaló @Oli, ¡el compilador no está obligado a garantizar esto!

  • Cabe señalar que el compilador no está obligado a asegurarse de que ambas estructuras estén empaquetadas y alineadas de la misma manera.

    –Oliver Charlesworth

    22 de octubre de 2010 a las 11:17

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

    – ranuradoandi

    22 de octubre de 2010 a las 11:33

  • @OliverCharlesworth: se necesitaría un compilador patológicamente perverso para romper esta suposición, especialmente. considerando la respuesta aceptada a esta pregunta: stackoverflow.com/questions/19804655/…

    – chqrlie

    12/09/2017 a las 19:46

  • @chqrlie La respuesta aceptada que vinculó solo dice que un compilador debe asegurarse de que el diseño sea igual, si ambas estructuras se usan como parte de la misma unión. Pero no dice nada sobre el uso de estructuras fuera de un contexto de unión. Entonces, un compilador puede imponer un diseño igual en un caso de unión, ya que tiene que hacerlo de acuerdo con el estándar, pero elegir diseños diferentes en otros casos debido a alguna extraña optimización específica de la plataforma, por ejemplo.

    – Mecki

    5 de junio de 2018 a las 14:26

avatar de usuario
diente filoso

Utiliza una función de utilidad para eso:

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

¿Por qué no usas

typedef CMAcceleration Vector3d;

(en lugar de crear una estructura completamente nueva)

en ese caso vector = acceleration; compila muy bien.

  • Yo tengo un warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage. También en este caso, CMAcceleration está en un marco débilmente vinculado, por lo que me abstengo de usarlo en mi archivo .h.

    –Ortwin Gentz

    22 de octubre de 2010 a las 11:50

  • Si el CMAcceleration struct proviene de un marco separado, se recomienda que haga la copia campo por campo, en lugar de los trucos de memcpy o juegos de palabras, para que su código sea sólido en caso de cambios futuros en el otro marco. (Incluso si sabe que los diseños de estructura son idénticos hoy, tal vez no sigan siendo así en versiones posteriores).

    – David Gelhar

    22 de octubre de 2010 a las 13:03

Esto se logra fácilmente a través de un 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);
}

  • Yo tengo un warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage. También en este caso, CMAcceleration está en un marco débilmente vinculado, por lo que me abstengo de usarlo en mi archivo .h.

    –Ortwin Gentz

    22 de octubre de 2010 a las 11:50

  • Si el CMAcceleration struct proviene de un marco separado, se recomienda que haga la copia campo por campo, en lugar de los trucos de memcpy o juegos de palabras, para que su código sea sólido en caso de cambios futuros en el otro marco. (Incluso si sabe que los diseños de estructura son idénticos hoy, tal vez no sigan siendo así en versiones posteriores).

    – David Gelhar

    22 de octubre de 2010 a las 13:03

avatar de usuario
lundblade

Otra versión de la función de utilidad que utiliza C99:

static inline struct Vector3d AccelerationToVector(struct CMAcceleration In)
{
    return (struct Vector3d){In.x, In.y, In.z};
}

Con la optimización del compilador activada (por ejemplo, -Os), esto debería convertirse en ningún código objeto cuando se invoque.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad