便携式数据重新解释
我想以便携方式(C99)将一种类型的数据重新解释为另一种类型。 我不是在谈论铸造,我想重新解释一些给定的数据。 此外,通过便携式我的意思是它不会破坏C99规则 – 我并不是说重新解释的值在所有系统上都是相同的。
我知道3种不同的重新解释数据的方法,但其中只有两种是可移植的:
-
这不是便携式的 – 它打破了严格的别名规则。
/* #1 Type Punning */ float float_value = 3.14; int *int_pointer = (int *)&float_value; int int_value = *int_pointer;
-
这是依赖于平台的,因为它在将
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;
-
应该没问题,只要
sizeof(int) == sizeof(float)
/* #3 Copying */ float float_value = 3.14; int int_value = 0; memcpy(&int_value, &float_value, sizeof(int_value));
我的问题:
- 它是否正确?
- 您是否知道以便携方式重新解释数据的其他方法?
解决方案2 是便携式的 – 通过工会的类型惩罚在C99中一直是合法的,并且它在TC3中明确说明,它在6.5.2.3节中添加了以下脚注:
如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为新类型中的对象表示forms在6.2.6中描述(一个过程有时称为“类型双关语”)。 这可能是陷阱表示。
附件J仍然将其列为未指明的行为,这是一个已知的缺陷,并已用C11进行了更正,后者发生了变化
存储在[ 未指定 ]中的最后一个以外的联合成员的值
至
对应于最后存储到[ 未指定 ]的联合成员之外的字节值
这并不是什么大不了的事,因为附件只是提供信息,而不是规范性的。
请记住,您仍然可能会遇到未定义的行为,例如
- 通过创建陷阱表示
- 在具有指针类型的成员的情况下违反别名规则(由于不需要统一的指针表示,因此不应该通过类型惩罚进行转换)
- 如果联合成员具有不同的大小 – 只有商店上次使用的成员的字节具有指定的值; 特别是,将值存储在较小的成员中也可以使较大成员的尾随字节无效
- 如果成员包含填充字节,则始终采用未指定的值
-
联合解决方案定义为C中的memcpy(AFAIK,它是C ++中的UB),参见DR283
-
可以将指针强制转换为指向(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)
,在这种情况下,您仍会获得未定义的行为