【问题标题】:How to cast C struct just another struct type if their memory size are equal?如果 C 结构的内存大小相等,如何将 C 结构转换为另一种结构类型?
【发布时间】:2010-05-02 09:28:48
【问题描述】:

我有 2 个矩阵结构意味着相同的数据,但具有不同的形式,如下所示:

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

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

【问题讨论】:

    标签: c casting struct unions


    【解决方案1】:

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

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

    【讨论】:

    • 太棒了。但在我看来,它需要一种代理变量。它不会导致一些复制吗?
    • 联合是最好的主意。最类型安全、最简单的转换,没有开销。
    • 如果密钥不同怎么办?顺序一样没问题吧?
    【解决方案2】:

    好吧,你可以像这样声明 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 只允许您在左值上使用 & 运算符。

    【讨论】:

    • 这是type punning 通过指针转换 - 它打破了别名规则,并且应该使用任何体面的编译器生成警告。请参阅:en.wikipedia.org/wiki/Type_punning。请注意,联合技巧也是严格意义上的 UB,但通常被认为比指针转换方法更安全。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多