为了账号安全,请及时绑定邮箱和手机立即绑定

为什么位Endianness是位域中的一个问题?

为什么位Endianness是位域中的一个问题?

C++ C
慕盖茨4494581 2019-06-20 17:33:35
为什么位Endianness是位域中的一个问题?任何使用位字段的可移植代码似乎都可以区分小平台和大端平台。见Linux内核中struct iphdr的声明这类代码的例子。我不明白为什么一点点迷恋是一个问题。据我所知,位字段是纯粹的编译器构造,用于方便位级操作。例如,考虑以下位域:struct ParsedInt {     unsigned int f1:1;     unsigned int f2:3;     unsigned int f3:4;};uint8_t i;struct ParsedInt *d = &i;在这里,写作d->f2是一种简洁易懂的表达方式(i>>1) &  (1<<4 - 1).但是,位操作是定义良好的,并且不管体系结构如何都能工作.那么,为什么位域不是可移植的呢?
查看完整描述

3 回答

?
墨色风雨

TA贡献1853条经验 获得超6个赞

按照C标准,编译器可以随意地以任意方式存储位字段。你可以的绝不可能对比特的分配位置做任何假设。下面是一些C标准没有指定的与位字段相关的内容:

未指定行为

  • 分配给容纳位字段的可寻址存储单元的对齐(6.7.2.1)。

实现-定义的行为

  • 位字段是否可以跨越存储单元边界(6.7.2.1).
  • 单元内位字段的分配顺序(6.7.2.1).

大/小终端当然也是实现定义的。这意味着您的结构可以按以下方式分配(假设16位INT):

PADDING : 8f1 : 1f2 : 3f3 : 4or

PADDING : 8f3 : 4f2 : 3f1 : 1or

f1 : 1f2 : 3f3 : 4PADDING : 8or

f3 : 4f2 : 3f1 : 1PADDING : 8

哪一个适用?猜一猜,或者深入阅读编译器的后端文档。再加上32位整数的复杂性,以大数或小数表示,再加上这一点。然后添加一个事实,即编译器可以添加任意数量的填充。字节在BIT字段中的任何位置,因为它被视为一个结构(它不能在结构的开头添加填充,但可以添加其他任何地方)。

然后,我甚至没有提到如果您使用普通的“int”作为位字段类型=实现定义的行为,或者使用(无符号)int=实现定义的行为以外的任何其他类型会发生什么。

因此,要回答这个问题,不存在可移植的位域代码,因为C标准对于如何实现位字段非常模糊。唯一可以信任的位字段是布尔值块,程序员不关心这些位在内存中的位置。

唯一的可移植解决方案是使用按位运算符而不是位字段。生成的机器代码将完全相同,但具有确定性。在任何C编译器上,任何系统的逐位操作符都是100%可移植的.


查看完整回答
反对 回复 2019-06-20
?
RISEBY

TA贡献1856条经验 获得超5个赞

据我所知,位域纯粹是编译器构造。

这也是问题的一部分。如果位字段的使用仅限于编译器“拥有”的内容,那么编译器如何打包或排序位将几乎与任何人无关。

然而,位字段可能更多地用于建模编译器域-硬件寄存器、通信的“连线”协议或文件格式布局的外部结构。这些东西对如何布局比特有严格的要求,而使用位字段对其建模意味着您必须依赖于实现定义的,更糟糕的是编译器将如何布局位字段的未指定的行为。

简而言之,位字段的指定不够好,不能使它们在最常用的情况下有用。


查看完整回答
反对 回复 2019-06-20
?
大话西游666

TA贡献1817条经验 获得超14个赞

ISO/IEC 9899: 6.7.2.1 / 10

实现可以分配任何足够大的可寻址存储单元,以容纳位fiLD。如果有足够的空间,结构中紧接着另一位的位fiLD应被封装在同一单元的相邻位中。如果不存在fi-fi空间,则将不适合的位-fiLD放入下一个单元或重叠相邻单元是实现-defiNed。在一个单元内(从高阶到低阶或低阶到高阶)位fi屏蔽的分配顺序是实现-defined。可寻址存储单元的对齐是fi编辑的。

在试图编写可移植代码时,使用位移位操作而不是对位字段的排序或对齐做任何假设是更安全的,而不考虑系统的endianness或Bitness。

亦见EXP11-C.不要将期望一种类型的运算符应用于不兼容类型的数据。


查看完整回答
反对 回复 2019-06-20
  • 3 回答
  • 0 关注
  • 549 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信