我需要以这种方式在一个字节中打包一些位:
struct
{
char bit0: 1;
char bit1: 1;
} a;
if( a.bit1 ) /* etc */
或:
if( a & 0x2 ) /* etc */
从源代码清晰度来看,对我来说很明显,bitfields更整洁。但哪个选项更快?我知道速度差异不会太大,但是因为我可以使用它们中的任何一个,如果一个更快,更好。
另一方面,我已经读过,不保证位域不能跨平台排列相同的位,我希望我的代码可以移植。
注意:如果您打算回答“个人资料”,我会,但是因为我很懒,如果有人已经有了答案,那就更好了。
代码可能有误,如果你愿意,可以纠正我,但请记住这个问题的重点是什么,请尝试回答。
答案 0 :(得分:12)
如果使用得当,Bitfields会使代码更清晰。我会使用位域作为节省空间的设备。我见过它们的一个常见地方是编译器:通常类型或符号信息由一堆真/假标志组成。 Bitfields在这里是理想的,因为典型的程序在编译时会创建数千个这样的节点。
我不会使用位域来执行常见的嵌入式编程工作:读取和写入器件寄存器。我更喜欢在这里使用shift和mask,因为你可以得到文档告诉你需要的一些内容,而且你不必担心各种编译器实现bitfields的差异。
至于速度,一个好的编译器会为屏蔽的位域提供相同的代码。
答案 1 :(得分:7)
我宁愿使用第二个例子而不是最大的可移植性。正如Neil Butterworth指出的那样,使用位域仅适用于原生处理器。好吧,想想看,如果英特尔的x86明天停业,会发生什么,代码将被卡住,这意味着必须重新实现另一个处理器的位域,RISC芯片说。
你必须从更大的角度来看看OpenBSD如何使用一个代码库将他们的BSD系统移植到很多平台?好吧,我承认这有点过头,有争议和主观,但实际上,如果你想将代码移植到另一个平台,它的方法是使用你在问题中使用的第二个例子
不仅如此,不同平台的编译器将有自己的填充方式,为编译器所在的处理器对齐位域。而且,处理器的endianess呢?
永远不要依赖比特场作为魔法子弹。如果您想要处理器的速度并将固定在它上,即无意移植,那么随意使用位域。 你不能同时拥有这两个!
答案 2 :(得分:6)
关于哪个更快:不相关。任何优化编译器(这几乎都意味着所有)将使代码以任何表示法执行相同的操作。这是C程序员的常见误解,编译器只会将关键字搜索和替换为汇编。现代编译器使用源代码作为应该实现的目标的蓝图,然后发出通常看起来非常不同但实现预期结果的代码。
答案 3 :(得分:5)
第一个是显式的,无论第二个表达式容易出错的速度如何,因为对结构的任何更改都可能使第二个表达式出错。
所以使用第一个。
答案 4 :(得分:4)
如果您想要便携性,请避免使用位域。如果您对特定代码的性能感兴趣,则无法编写自己的测试。请记住,位域将使用处理器的按位指令。
答案 5 :(得分:1)
我认为C程序员倾向于使用位掩码和逻辑运算来推断每个位的值。不是让代码乱丢十六进制值,而是设置枚举,或者,通常在涉及更复杂的操作时,使用宏来获取/设置特定位。我听说过小道消息,结构实现的位域比较慢。
答案 6 :(得分:1)
不要在“非便携式位字段”中阅读太多内容。存在实现定义的位字段的两个方面:签名和布局以及一个未指定的方面:打包它们的分配单元的对齐。如果您不需要任何其他包装效果,使用它们是可移植的(如果您在需要时明确指定了signed
关键字)作为函数调用,它们也具有未指定的属性。
关于表现,个人资料是您可以获得的最佳答案。在一个完美的世界里,两篇着作之间没有区别。在实践中,可能有一些,但我可以想到在另一个方向上有多少理由。它可能对上下文非常敏感(无符号和签名之间在逻辑上毫无意义的区别)因此在上下文中进行测量...
总而言之,在您真正做出选择的情况下,差异主要是风格差异(即,如果精确布局很重要,则不一样)。在这种情况下,它是一个优化(大小,而不是速度),所以我倾向于首先编写没有它的代码,并在需要后添加它。因此,位字段是显而易见的选择(要完成的修改是实现结果的最小修改,并且包含在定义的唯一位置而不是传播到所有使用位置)。