便携式数据重新解释

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

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

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

    /* #1 Type Punning */ float float_value = 3.14; int *int_pointer = (int *)&float_value; int int_value = *int_pointer; 
  2. 这是依赖于平台的,因为它在将float写入其中之后从union中读取int值。 但它不会破坏任何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)); 

我的问题:

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

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

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

附件J仍然将其列为未指明的行为,这是一个已知的缺陷,并已用C11进行了更正,后者发生了变化

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

对应于最后存储到[ 未指定 ]的联合成员之外的字节值

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

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

  • 通过创建陷阱表示
  • 在具有指针类型的成员的情况下违反别名规则(由于不需要统一的指针表示,因此不应该通过类型惩罚进行转换)
  • 如果联合成员具有不同的大小 – 只有商店上次使用的成员的字节具有指定的值; 特别是,将值存储在较小的成员中也可以使较大成员的尾随字节无效
  • 如果成员包含填充字节,则始终采用未指定的值
  1. 联合解决方案定义为C中的memcpy(AFAIK,它是C ++中的UB),参见DR283

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

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

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

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

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

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

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

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

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