【问题标题】:Casting one C structure into another将一个 C 结构转换为另一个
【发布时间】:2011-04-29 02:21:42
【问题描述】:

我有两个相同(但名称不同)的 C 结构:

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


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

现在我想将 CMAcceleration 变量分配给 Vector3d 变量(复制整个结构)。我该怎么做?

我尝试了以下方法,但得到了这些编译器错误:

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

当然我可以单独设置所有成员:

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

但这似乎很不方便。

什么是最好的解决方案?

【问题讨论】:

  • 你不能只是 typedef(比如 typedef struct CMAcceleration Vector3d)吗?哎呀,已经有人指出了...

标签: c casting struct


【解决方案1】:

您可以使用指针进行类型转换;

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

【讨论】:

  • 应该指出,编译器没有义务确保两个结构都以相同的方式打包和对齐。
  • 由于严格的别名,这是未定义的行为。 cellperformance.beyond3d.com/articles/2006/06/…
  • @Secure 真可惜,因为我想使用这种技术来不复制但实际上是别名(更改类型)结构。
  • @Michael:指定在使用 gcc 或 clang 时,必须使用 -fno-strict-aliasing 标志编译代码(其他编译器可能使用同名标志,或者在别名方面可能不那么激进比 gcc 或 clang 更优化)。
【解决方案2】:

你为此使用了一个实用函数:

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

【讨论】:

    【解决方案3】:

    这是你唯一的解决方案(除了将它包装成一个函数):

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

    你实际上可以像这样(使用指针)投射它

    Vector3d *vector = (Vector3d*) &acceleration;
    

    但这不在规范中,因此行为取决于编译器、运行时和大型绿色空间怪物。

    【讨论】:

    • +1:很好的答案。描述了唯一可以保证有效的方法和通常在实践中有效的方法,以及该方法在技术上未定义的原因。
    • +1 我只想补充一点,铸造技术很常见——它不像它真正的邪恶。
    • +1 用于将其包装在一个函数中。即使是这样微不足道的事情也值得为其创建一个子例程。
    • 如果我们将 CMAcceleration 声明为 struct { Vector3d vec; }; 会发生什么?然后 CMAcceleration 实例将在第一个 sizeof(Vector3d) 字节中具有 Vector3d。这会在执行指针转换时消除严格的别名吗?
    • 那么我们就不需要再转换指针了。我们可以直接分配vector = acc.vec;
    【解决方案4】:

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

    请注意,这仅适用于内存中结构的物理布局相同的情况。但是,正如@Oli 指出的那样,编译器没有义务确保这一点!

    【讨论】:

    • 应该指出,编译器没有义务确保两个结构都以相同的方式打包和对齐。
    • @Oli Charlesworth:你是对的,我相应地更新了答案
    • @OliverCharlesworth:需要一个病态的反常编译器才能打破这个假设,尤其是。考虑到这个问题的公认答案:stackoverflow.com/questions/19804655/…
    • @chqrlie 您链接的接受的答案仅说如果两个结构都用作同一联合的一部分,编译器必须确保布局相等。但它没有说明在联合上下文之外使用结构。因此,编译器可能会在联合情况下强制执行相同的布局,因为它必须根据标准,但在其他情况下会因为某些奇怪的平台特定优化而选择不同的布局。
    【解决方案5】:

    你为什么不使用。

    typedef CMAcceleration Vector3d;
    

    (而不是创建一个全新的结构)

    在这种情况下,vector = acceleration; 编译得很好。

    【讨论】:

    • 我收到了warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage。同样在这种情况下,CMAcceleration 处于弱链接框架中,因此我避免在我的 .h 文件中使用它。
    • 如果 CMAcceleration 结构来自单独的框架,最好建议您进行逐字段复制,而不是使用 memcpy 或类型双关技巧来制作您的代码在其他框架未来发生任何变化的情况下都具有强大的功能。 (即使你知道今天的结构布局是相同的,也许它们在后续版本中不会保持这种状态。)
    【解决方案6】:

    一种安全(尽管有些复杂)的方法是使用联合:

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

    当访问的成员不是最后一个集合时,重新解释值(自 C99 起)。在这种情况下,我们设置了加速度,然后我们访问了向量,因此重新解释了加速度。

    这就是NSRectToCGRect函数的实现方式,例如。

    【讨论】:

      【解决方案7】:

      这可以通过联合轻松实现:

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

      【讨论】:

      • 请注意,如果您这样做,您将不再遵守标准 C。是的,GCC 和 Clang 都支持访问非当前工会成员。不,标准不允许这样做。
      【解决方案8】:

      使用 C99 的实用函数的另一个版本:

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

      随着编译器优化出现(例如,-Os),这应该在调用时变成绝对没有目标代码。

      【讨论】:

        【解决方案9】:

        您可以使用指针创建联合,以避免复制数据。

        示例:

        struct Ocp1SyncHeader
        {
            aes70::OCP1::OcaUint8 syncVal;          
            aes70::OCP1::Ocp1Header header;
        };
        
        struct convertToOcp1SyncHeader
        {
            union
            {
                Ocp1SyncHeader* data;
                const uint8_t* src;
            };
            convertToOcp1SyncHeader& operator=(const uint8_t* rvalue) 
            { 
              src = (const uint8_t*)rvalue; 
              return *this; 
            }
        };
        

        ** 像这样访问:

        convertToOcp1SyncHeader RecvData;
        RecvData = src;  // src is your block of data that you want to access
        

        ** 像这样访问成员:

        RecvData.data.header
        

        【讨论】:

          猜你喜欢
          • 2011-04-15
          • 1970-01-01
          • 2011-04-08
          • 2023-03-16
          • 2020-11-17
          • 2011-01-17
          • 2022-06-10
          • 2020-06-30
          • 1970-01-01
          相关资源
          最近更新 更多