为什么这个联合的大小是2的位域?

我在Windows上的turbo C上工作,其中char占用一个字节。现在我的问题是下面的联合。

union a { unsigned char c:2; }b; void main() { printf("%d",sizeof(b)); \\or even sizeof(union a) } 

该程序打印输出为2,其中联合应该只占用1个字节。 为什么会这样?

对于struct,它可以很好地给出1个字节但是这个联合工作不正常。

还有一件事如何访问这些位字段。

 scanf("%d",&b.c); //even scanf("%x",bc); 

没有用,因为我们不能有位的地址。所以我们必须使用另一个变量,如下所示

 int x; scanf("%d",&x); bc=x; 

我们不能避免吗? 有没有其他办法???

Turbo C基于8086微处理器,具有两个字节的字边界 。 primefaces读取和写入通常绑定到CPU的体系结构,因此编译器会添加一些松弛字节来对齐数据结构。

替代文字

调用#pragma pack(1)可能会禁用它,但不确定它是否适用于Turbo C.

允许编译器向结构和联合添加填充,而我承认,当你能够获得一个完全允许的单字节结构时,你的联盟会将联合四舍五入到两个字节大小,这有点令人惊讶。

回答你的第二个问题:不,这是不可避免的。 位字段是结构打包优化,并且要支付的性能和便利性惩罚是位字段成员不能单独寻址。

我不确定你在哪里找到联合必须精确到最小尺寸的要求。 对象必须至少与其成员一样大,但这只是一个下限。

你不能拿一个位域的地址; 它的类型是什么? 它不能是int *。 scanf(%d)会将sizeof(int)* CHAR_BIT位写入您传入的int *。这是写入超过2位,但您没有该空间。

标准中有一个段落声明在结构的第一个成员之前不应有填充。 但它没有明确说明工会。 大小的差异可能是因为它想要以2字节边界对齐并且因为它不能在结构的第一个成员之前填充,所以结构将具有一个字节对齐。 还要注意,联合可以有更多不同类型的成员,这可以扩大联盟所需的对齐方式。 可能有理由让编译器给它们至少2个字节对​​齐,例如以简化必须根据所需的联合对象处理的代码。

无论如何,没有要求你的联合应该是一个字节。 它必须为所有成员提供场所。

以下是C标准对您的第二个问题的看法:

The operand of the unary & operator shall be either a function designator or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

所以你最好的选择是用你的方式使用int。 你可以在代码周围加上大括号,所以临时变量保持在本地:

 void func(void) { struct bits f; { int x; scanf("%d", &x); f.bitfield = x; } /* ... */ } 

答案中有很多错误的信息,所以我会澄清。 这可能是由于两个原因之一(我不熟悉编译器)。

  1. 位域存储单元是2。

  2. 对齐被强制为字(2字节)边界。

我怀疑这是第一种情况,因为将位域存储单元作为声明的“基本”类型的大小是一种常见的扩展。 在这种情况下,类型是char,其大小始终为1。

[在标准中,您只能声明int或unsigned int类型的位域,并且其中位域分组的“存储单元”是固定的(通常与int的大小相同)。 即使是单个位位域也将使用一个存储单元。

在第二种情况下,C编译器通常实现#pragma pack以允许控制对齐。 我怀疑默认打包是2,在这种情况下,将在联合的末尾添加填充字节。 避免这种情况的方法是使用:

 #pragma pack(1) 

您之后还应该使用#pragma pack()来设置回默认值(如果编译器支持,甚至可以更好地使用push和pop参数)。

对于那些说你必须忍受编译器工作的所有回复者来说,这与C的精神相反。在你无法控制它的情况下,你应该能够使用位域映射到任何大小或位顺序例如文件格式或硬件映射。

当然,这是高度不可移植的,因为不同的实现具有不同的字节顺序,将位添加到位域存储单元(从顶部或底部),存储单元大小,默认对齐等的命令。

至于你的第二个问题,我看不出问题,虽然我从不使用scanf因为它有问题。

除了“在结构或联合的末尾还可能存在未命名的填充”这一事实之外,编译器还允许在“任何可寻址的存储单元中放置一个足够大的位域来保存位字段”。 (这两个引用均来自C90标准 – C99标准中有类似但不同的措辞)。

另请注意,标准表示“位字段的类型必须是int,unsigned int或signed int的限定或非限定版本”,因此在char类型中使用位字段是非标准的。

因为位域的行为如此依赖于未指定的编译器实现细节(还有其他几个我没有提到的比特字段的非可移植问题)使用它们几乎总是一个坏主意。 特别是,当您尝试对文件格式,网络协议或硬件寄存器中的位字段建模时,它们是一个坏主意。


来自另一个SO答案的更多信息:

通常,您应该避免使用位域并使用其他显式常量(枚举或其他)与显式位屏蔽并移位以访问字段中的“子字段”。

这就是为什么应该避免使用位域的一个原因 – 即使对于同一平台,它们在编译器之间也不是很容易移植。 来自C99标准(C90标准中有类似的措辞):

实现可以分配任何足够大的可寻址存储单元来保持位域。 如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中。 如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的。 单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的。 未指定可寻址存储单元的对齐。

您无法保证位字段是否会“跨越”int边界,并且您无法指定位域是从int的低端开始还是从int的高端开始(这与处理器是否为独立无关) big-endian或little-endian)。