2010-05-02 3 views
10

tengo 2 estructuras de matriz significa datos iguales pero tienen forma diferente como los siguientes:¿Cómo convertir C struct a otro tipo de estructura si el tamaño de su memoria es el mismo?

// Matrix type 1. 
typedef float Scalar; 
typedef struct { Scalar e[4]; } Vector; 
typedef struct { Vector e[4]; } Matrix; 

// Matrix type 2 (you may know this if you're iPhone developer) 
// Defines CGFloat as float for simple description. 
typedef float CGFloat; 
struct CATransform3D 
    { 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 
typedef struct CATransform3D CATransform3D; 

Sus tamaños de memoria son iguales. Así que yo creo que es una forma de convertir estos tipos sin ningún tipo de operaciones de puntero o copiar la siguiente manera:

// Implemented in external lib. 
CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 
Matrix m = (Matrix)CATransform3DMakeScale (1, 2, 3); 

Es esto posible? Actualmente el compilador imprime un mensaje de "error: conversión a tipo no escalar solicitado".

Respuesta

16

Probablemente la mejor solución sería combinar sus dos estructuras en una unión. Si desea interpretar los mismos datos de dos maneras diferentes, entonces es la elección obvia. La alternativa es usar lanzamientos de punteros, pero eso es feo, rompe las reglas de alias y puede ocultar los errores que el compilador podría informar.

typedef float Scalar; 
typedef struct { Scalar e[4]; } Vector; 
typedef struct { Vector e[4]; } Matrix; 

struct CATransform3D 
{ 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 
typedef struct CATransform3D CATransform3D; 

typedef union 
{ 
    CATransform3D t; 
    Matrix m; 
} UMatrix; 

CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 
UMatrix um; 
um.t = CATransform3DMakeScale (1, 2, 3); 
// 
// now you can just use um.m when you need to refer to the Matrix type... 
// 
+1

Brillante. Pero en mi opinión, requiere un tipo de variable proxy. ¿No causa algo de copia? – Eonil

+0

No, simplemente defina una unión como typedef - vea el ejemplo anterior. –

+1

La unión es la mejor idea. La mayoría de las conversiones de tipo seguro, más simple, sin gastos generales. – Puppy

1

Bueno, sólo podría declarar CATransform3DMakeScale así:

Matrix CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 

C no escriba a comprobar para asegurarse de que coincide con la desaceleración de la biblioteca original. Si el diseño de la memoria es el mismo, funcionará. Sin embargo, es una mala práctica de codificación y debe incluir un comentario extenso que justifique sus acciones. ;-)

De lo contrario, no hay forma de evitarlo: tiene que usar punteros o copiar los datos. Esto funcionaría:

CATransform3D m3d = CATransform3DMakeScale (1, 2, 3); 
Matrix m; 
memcpy(&m, &m3d, sizeof m); 

Como has descubierto, no puedes transmitir la estructura directamente. Tampoco se puede hacer esto:

Matrix m = *(Matrix*) &CATransform3DMakeScale (1, 2, 3); 

porque C sólo se le permite usar el operador & en una l-valor.

+1

Esto es 'tipo de juego de palabras 'a través de modelos de puntero: rompe las reglas de aliasing y debe generar una advertencia con cualquier compilador decente. Ver: http://en.wikipedia.org/wiki/Type_punning. Tenga en cuenta que el truco de unión también es estrictamente UB, pero generalmente se considera más seguro que el método de lanzamiento de puntero. –

+1

@Paul R: Buen punto. He actualizado mi respuesta. –

Cuestiones relacionadas