如何将C结构体转换为另一个结构体类型,如果它们的内存大小相等?

10

我有两个矩阵结构体,它们的数据相同但形式不同,就像这样:

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

它们的内存大小相等。因此,我相信有一种方法可以在不使用任何指针操作或复制的情况下转换这些类型:

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

这是否可能?当前编译器会打印出“错误:请求转换为非标量类型”消息。

2个回答

16

可能最好的解决方案是将您的两个结构体组合成一个联合体。如果您想以两种不同的方式解释相同的数据,那么这是显而易见的选择。另一种选择是使用指针转换,但那样很丑陋,违反了别名规则,并且可能隐藏编译器本来会报告的错误。

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
太棒了。但是在我看来,它需要一种代理变量。这会导致一些复制吗? - eonil
1
联合是最好的想法。最安全的类型转换,最简单的转换,没有额外开销。 - Puppy
如果键不同怎么办?如果它们是按照相同的顺序排列,那就没有问题了。 - hfossli

1

嗯,你可以这样声明CATransform3DMakeScale:

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

C语言不会进行类型检查以确保您的声明与原始库匹配。如果内存布局相同,则可以正常工作。但是,这是一种不好的编码实践,您应该包含一个长注释来证明您的行为是正确的。 ;-)

否则,没有其他办法:您必须使用指针或复制数据。以下代码可以解决问题:

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

正如你所发现的那样,你不能直接转换结构。你也不能这样做:

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

因为C语言只允许在左值上使用&运算符。


1
这是通过指针转换进行的“类型游戏” - 它违反了别名规则,并且应该在任何体面的编译器中生成警告。请参见:http://en.wikipedia.org/wiki/Type_punning。请注意,联合技巧也是严格的未定义行为,但通常被认为比指针转换方法更安全。 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接