便携式数据重新解释

时间:2011-12-14 21:08:29

标签: c type-punning

我想以便携方式(C99)将一种类型的数据重新解释为另一种类型。 我谈论投射,我想要一些给定数据的重新解释。 此外,通过 portable 我的意思是它不会破坏C99规则 - 我意味着重新解释的值在所有系统上都是相同的。

我知道3种不同的重新解释数据的方法,但其中只有两种是可移植的:

  1. 这不是便携式的 - 它打破了严格的别名规则。

    /* #1 Type Punning */
    
    float float_value = 3.14;
    int *int_pointer = (int *)&float_value;
    int int_value = *int_pointer;
    
  2. 这是依赖于平台的,因为它在写入int后会从联合中读取float值。但它不会违反任何C99规则,因此应该有效(如果sizeof(int) == sizeof(float))。

    /* #2 Union Punning */
    
    union data {
      float float_value;
      int int_value;
    };
    
    union data data_value;
    data_value.float_value = 3.14;
    int int_value = data_value.int_value;
    
  3. 只要sizeof(int) == sizeof(float)

    ,就可以了
    /* #3 Copying */
    
    float float_value = 3.14;
    int int_value = 0;
    memcpy(&int_value, &float_value, sizeof(int_value));
    
  4. 我的问题:

    1. 这是对的吗?
    2. 您是否知道以便携式方式重新解释数据的其他方法?

5 个答案:

答案 0 :(得分:18)

解决方案2 便携式 - 通过工会的类型惩罚在C99中一直是合法的,并且它在TC3中明确说明,它在6.5.2.3节中添加了以下脚注:

  

如果用于访问union对象内容的成员不是   与上次用于在对象中存储值的成员相同,   值的对象表示的适当部分是   如上所述,重新解释为新类型中的对象表示   在6.2.6(一个过程有时被称为"类型双关语")。这可能是一个   陷阱表示。

附件J仍然将其列为未指明的行为,这是一个已知的缺陷,并已通过C11更正,但已更改

  

存储在[中的最后一个工会成员以外的工会成员的值未指定]

  

与最后存储的成员之外的union成员对应的字节值   进入[未指定]

这并不是什么大不了的事,因为附件只是提供信息,而不是规范性的。

请记住,您仍然可能会遇到未定义的行为,例如

  • 创建陷阱表示
  • 在成员具有指针类型的情况下违反别名规则(无论如何都不应通过类型惩罚转换,因为不需要统一的指针表示)
  • 如果联盟成员具有不同的大小 - 只有商店上次使用的成员的字节具有指定的值;特别是,将值存储在较小的成员中也会使较大成员的尾随字节无效
  • 如果成员包含填充字节,它总是采用未指定的值

答案 1 :(得分:2)

  1. 联合解决方案定义为C中的memcpy(AFAIK,C ++中的UB),请参阅DR283

  2. 可以将指针强制转换为指向(signed / unsigned /)char的指针,所以

    unsigned char *ptr = (unsigned char*)&floatVar;
    

    然后访问ptr [0]到ptr [sizeof(floatVar)-1]是合法的。

答案 2 :(得分:0)

为了安全起见,我会使用字节数组(unsigned char)而不是'int'来保存值。

答案 3 :(得分:0)

数据类型int是非可移植类型的示例,因为字节顺序可以更改平台之间的字节顺序。

如果您想要可移植,则需要定义自己的类型,然后在要移植到的每个平台上实现它们。然后为您的数据类型定义转换方法。据我所知,这是完全控制字节顺序等的唯一方法。

答案 4 :(得分:0)

如果要避免严格别名规则,则需要先转换为char指针:

float float_value = 3.14;
int *int_pointer = (int *)(char *)&float_value;
int int_value = *int_pointer;

但请注意,您可能拥有sizeof(int) > sizeof(float),在这种情况下,您仍会获得未定义的行为