C位字段变量正在打印意外值

Kee*_*ath 5 c bit

struct m

{
   int parent:3;

   int child:3;

   int mother:2;
};

void main()
{

   struct m son={2,-6,5};

   printf("%d %d %d",son.parent,son.child,son.mother);
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮忙说出为什么程序的输出是2 2 1

Who*_*aig 26

取出显示的字段的所有重要位:

parent: 3 bits (1-sign bit + 2 more), value 010, result 2
child:  3 bits (1-sign bit + 2 more), value 010, result 2
mother: 2 bits (1 sign bit + 1 more), value  01, result 1
Run Code Online (Sandbox Code Playgroud)

细节

它指出您的结构字段被声明为int位字段值.通过C99-§6.7.2,2,以下类型都是相同的:int,signedsigned int.因此,您的结构字段已签名.根据C99-§6.2.6.2,2,您的一个比特应该用于表示变量的"符号"(负数或正数).此外,相同的部分指出除了符号位之外,剩余的位表示必须对应于剩余位计数的相关无符号类型.C99-§6.7.2,1清楚地定义了每个位如何表示2的幂.因此,通常用作符号位的唯一位是最高位(它是唯一剩下的位,但是我很确定这是否是对我将在适当的时候听到的标准的不准确解释.您将负数指定为用于样本的测试值之一表明您可能已经意识到这一点,但许多新暴露于位字段的人不是.因此,值得注意.

本答案的其余部分引用了C99标准的以下部分.第一个涉及不同类型的促销,下一个是估值和潜在的价值变化(如果有的话).最后一点对于理解如何int确定位字段类型很重要.

C99-§6.3.1.1:布尔值,字符和整数

2:如果a int可以表示原始类型的所有值(由宽度限制,对于位字段),该值将转换为a int; 否则,它被转换为unsigned int.这些被称为整数促销.整数促销不会更改所有其他类型.

C99-§6.3.1.3有符号和无符号整数

  1. 当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变.
  2. 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.
  3. 否则,新类型将被签名,并且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号.

C99-§6.7.2.1结构和联盟说明

10:位字段被解释为具有由指定位数组成的有符号或无符号整数类型.如果将值0或1存储到_Bool类型的非零宽度位字段中,则位字段的值应等于存储的值; _Bool位字段具有_Bool的语义.

考虑int测试值的常规位表示.以下是32位int实现:

value : s  bits 
    2 : 0  0000000 00000000 00000000 00000010   <== note bottom three bits
   -6 : 1  1111111 11111111 11111111 11111010   <== note bottom three bits
    5 : 0  0000000 00000000 00000000 00000101   <== note bottom two bits
Run Code Online (Sandbox Code Playgroud)

遍历其中的每一个,应用上述标准参考的要求.

int parent:3:第一个字段是3位有符号int,并被赋予十进制值2.rvalue类型是否int包含左值类型int:3?是的,所以类型很好.该 是否2适合左值类型的范围?嗯,2很容易适应int:3,所以也不需要价值.第一个字段工作正常.

int child:3:第二个字段也是3位有符号int,这次分配了十进制值-6.再一次,rvalue type(int)是否完全包含左值类型(int:3)?是的,所以类型也很好.但是,最小位数需要表示-6有符号值,即4位.(1010),将最重要的位作为符号位.因此,该值-6超出了3位有符号位域的允许存储范围.因此,结果是根据§6.3.1.3-3 实现定义的.

int mother:2最后一个字段是2位有符号的int,这次被赋予十进制值5.再次,rvalue类型(int)是否完全包含左值类型(int:2)?是的,所以类型也很好.但是,我们再一次遇到了一个不适合目标类型的值.表示有符号正数的最小位数需要5是四:(0101).我们只有两个人合作.因此,结果再次按照§6.3.1.3-3 实现定义.

因此,如果我正确地理解这一点,那么在这种情况下的实现只会破坏除了存储填充声明的位深度所需的所有位之外的所有位.那个hackery的结果就是你现在拥有的.2 2 1

注意

完全有可能我错误地推翻了促销的顺序(我很容易迷失在标准中,因为我是阅读困难并且定期在我脑中翻动).如果是这种情况,我会问任何对标准有更强解释的人,请指出我的意见,我将相应地解决答案.


Joh*_*ode 2

你不能只用 3 位来表示 -6;同样,你不能只用两位来表示 5。

parentchildmother需要位字段 有什么原因吗?