位字段可移植性

问题描述 投票:0回答:4

我在here读到位字段不可移植。这是否意味着下面定义位字段的代码(代码取自here)无法在某些机器上编译?

如果是这样,那为什么?

#include <stdio.h>
#include <string.h>

/* define simple structure */
struct
{
  unsigned int widthValidated;
  unsigned int heightValidated;
} status1;

/* define a structure with bit fields */
struct
{
  unsigned int widthValidated : 1;
  unsigned int heightValidated : 1;
} status2;

int main( )
{
   printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
   printf( "Memory size occupied by status2 : %d\n", sizeof(status2));

   return 0;
}
c portability bit-fields
4个回答
28
投票

位字段是可移植的,因为它们是标准中指定的 C 语言的一部分(C11 第 6.7.2.1 节)。任何无法识别使用位域的代码的编译器都是不符合标准的。您的示例也没有什么真正值得怀疑的,因为它所做的只是存在位字段。

它们可能的意思是,字段本身的位置和顺序可能无法预测地打包(标准允许,之前的参考文献第 11 段)。这意味着具有例如的结构大小为 4、12、13 和 3 的四个位域不一定占用 32 位,并且它们不一定按该顺序放置在结构中;编译器可以将它们放在它喜欢的地方。这意味着该结构不能被视为底层二进制对象的实际组件方式表示。

相比之下,手动应用于整数的位掩码恰好存在于您放置它们的位置。如果您定义掩码来屏蔽无符号整数的前 4 位、后 12 位等,则“字段”实际上将按顺序和位置应用于这些位(无论如何,假设您知道字节序)。这使得表示独立于编译器。

即它们是便携式的,但它们所做的可能不一定正是一个人真正想要操纵各个位可能需要的。


13
投票

位字段是标准语言功能。它们将在所有 C 编译器中编译。从这个意义上来说,它们是可移植的。您的代码格式良好,也可以在所有 C 编译器中编译。

“位域不可移植”之类的语句通常意味着内存中位域的物理布局可能因实现而异(即从一个编译器到另一个编译器)。如果在不同的实现上编译,您可能会从程序中得到不同的输出。但是,只有当您的代码取决于具有位字段的对象的内存布局时(例如,如果您测量它们的大小,就像在程序中所做的那样),程序行为的差异才会发生。

换句话说,说“位域不可移植”与说类型

int
“不可移植”几乎是同一件事,只是因为它在不同平台上可以有不同的大小或在不同的平台上使用不同的字节序。它的内部表示。


3
投票

在 C 中,

int
要求至少为 16 位。因此,如果您希望在最大可移植代码中使用位字段,则不能拥有占用超过 16 位的位字段。

C.11 §6.7.2.1¶5:

位字段的类型应为

_Bool
signed int
unsigned int
的限定或非限定版本,或某些其他实现定义的类型。

C.11 §5.4.2.1¶1:

— 类型对象的最大值

int

INT_MAX +32767 //
215 − 1
— 类型对象的最大值
unsigned int

UINT_MAX 65535 //
216 − 1


0
投票

我主要在嵌入式领域工作,当您需要使用硬件寄存器和位标志时,位字段可以消除很多繁琐的位摆弄。

我不明白对位字段的巨大推动,每次都参考标准以及您应该如何滚动自己的标志,因为这样更便携;它很容易出错而且丑陋。

我确实了解位带的概念和优点,但这是特定于微控制器的。

比起强制执行命令的标准,我更看重“一致性”,尤其是“断言”你的假设是正确的。因此,如果您对位字段有一定的用途,请继续检查您的布局是否与事实相对应。例如 #include <stdint.h> #include <assert.h> #include <stdalign.h> typedef struct Foo_t { union { struct { uint16_t size : 9; uint16_t flag : 1; uint16_t type : 3; uint16_t prop : 3; }; uint16_t raw; }; } Foo_t; static void checkbits(void) { Foo_t Foo; assert(sizeof(Foo_t) == sizeof(uint16_t)); assert(alignof(Foo_t) == sizeof(uint16_t)); Foo.size = 365; assert((Foo.raw & 0b111111111) == Foo.size); Foo.prop = 0b110; Foo.type = 0b001; assert(((Foo.raw & 0xE000) >> 13) == Foo.prop); assert(((Foo.raw & 0x1C00) >> 10) == Foo.type); // ... rest to do as an exercise ... } 注意:您确实在代码中大量使用了assert(),是吗?

调用 checkbits() 函数就可以了;如果在具有特定编译器的系统上,假设不成立,请使用宏和重新排列的结构,直到原始格式再次正常。

请注意 checkbits() 代码中充实各个部分以进行检查是多么繁琐。您只需要完成一次繁琐的工作,在检查例程中,您会仔细检查,然后您就可以像小提琴一样自由地编写其余代码。

© www.soinside.com 2019 - 2024. All rights reserved.