C 中用于有符号整数结构 num 的位域 { int a:3;整数 b:2;整数 c:1; }
Bit fields in C for signed integer-struct num { int a:3; int b:2; int c:1; }
我对 C 中的位域感到困惑。任何人都可以向我解释位域的概念,或者任何人都可以建议我一些好的资源来理解结构中位域的基本概念吗?
我试过这个程序,但我无法理解输出。
在 gcc 32 位编译器中
struct num
{
int a:3;
int b:2;
int c:1;
};
void main()
{
struct num n={8,-6,5};
printf("%d\n%d\n%d",n.a,n.b,n.c);
}
输出:0,-2,-1
位域是一种 "tiny" 整数类型,您可以直接为其指定精确的位大小。位域只能在结构内部使用。位域的主要用途是 (a) 在结构中保存 space,您将有很多处理,以及 (b) 尝试确认外部强加的存储布局,例如设备驱动程序控制和状态寄存器或网络数据包。
您提供的代码片段给出了意想不到的结果,因为数字 8(以 2 为基数的 1000)不能用 3 位表示,-6 不能用 2 位表示,5 显然不能用 1 位表示.
好的,因为你是初学者,让我们试着解释一下..
第一个是 int a : 3
这意味着您有兴趣存储 3 位,但主要是您分配值 8..
现在 8 的二进制值为 1000,但由于您只考虑 3 位,因此采用最后 3 个零 (0) 并且输出为 0
现在第二个是 int b : 2
,这意味着您要存储 2 位。现在从主要你写-6。现在,每当任何负数存储在内存中时,它都存储为 2's complement
。因此 6 的二进制值是 00000000 00000000 00000000 00000110
并且因为它是负数,因此 1's complement
是 11111111 11111111 11111111 11111001
并且如果为 2's complement
添加 1
它将是 11111111 11111111 11111111 11111010
..现在因为你只在结构中使用 2 位,因此最后一位将是 10
或 2
的答案,因为它实际上是一个负数number 因此输出将是 -2
现在内存的第 31 位已经设置好,因此无论你给出什么新数字,它都会被视为负数..现在 c 是 1 位并且主要是你给出 5 这基本上是 0101
& 因此它将是最后一位 & 因此答案将是 -1
...
因此您的编译器运行正常..
首先,在位域声明中int
并不一定意味着signed int
。在位域上下文中,plain int
的符号是实现定义的。如果要确保存储值的符号性,则必须明确地说 signed [int]
是 C 语言中的一种情况。
其次,显然在您的情况下 int
位域恰好被签名,这意味着在 2 的补码平台上您的位域将具有以下范围
- 一个 : [-4, 3]
- b : [-2, 1]
- c : [-1, 0]
您分配的值不符合这些范围,导致有符号整数溢出。赋值时有符号整数溢出的结果是实现定义的。在您的实现中,2 的补码表示显然被简单地截断为可用位数(采用最低有效位)。
例如,8
在二进制中是 1000
,在截断为 3 位后,我们得到 000
。 -6
在 2 的补码二进制中是 ...1111010
,截断后我们得到 10
,这又是 -2
。等等。
我对 C 中的位域感到困惑。任何人都可以向我解释位域的概念,或者任何人都可以建议我一些好的资源来理解结构中位域的基本概念吗?
我试过这个程序,但我无法理解输出。
在 gcc 32 位编译器中
struct num
{
int a:3;
int b:2;
int c:1;
};
void main()
{
struct num n={8,-6,5};
printf("%d\n%d\n%d",n.a,n.b,n.c);
}
输出:0,-2,-1
位域是一种 "tiny" 整数类型,您可以直接为其指定精确的位大小。位域只能在结构内部使用。位域的主要用途是 (a) 在结构中保存 space,您将有很多处理,以及 (b) 尝试确认外部强加的存储布局,例如设备驱动程序控制和状态寄存器或网络数据包。
您提供的代码片段给出了意想不到的结果,因为数字 8(以 2 为基数的 1000)不能用 3 位表示,-6 不能用 2 位表示,5 显然不能用 1 位表示.
好的,因为你是初学者,让我们试着解释一下..
第一个是 int a : 3
这意味着您有兴趣存储 3 位,但主要是您分配值 8..
现在 8 的二进制值为 1000,但由于您只考虑 3 位,因此采用最后 3 个零 (0) 并且输出为 0
现在第二个是 int b : 2
,这意味着您要存储 2 位。现在从主要你写-6。现在,每当任何负数存储在内存中时,它都存储为 2's complement
。因此 6 的二进制值是 00000000 00000000 00000000 00000110
并且因为它是负数,因此 1's complement
是 11111111 11111111 11111111 11111001
并且如果为 2's complement
添加 1
它将是 11111111 11111111 11111111 11111010
..现在因为你只在结构中使用 2 位,因此最后一位将是 10
或 2
的答案,因为它实际上是一个负数number 因此输出将是 -2
现在内存的第 31 位已经设置好,因此无论你给出什么新数字,它都会被视为负数..现在 c 是 1 位并且主要是你给出 5 这基本上是 0101
& 因此它将是最后一位 & 因此答案将是 -1
...
因此您的编译器运行正常..
首先,在位域声明中int
并不一定意味着signed int
。在位域上下文中,plain int
的符号是实现定义的。如果要确保存储值的符号性,则必须明确地说 signed [int]
是 C 语言中的一种情况。
其次,显然在您的情况下 int
位域恰好被签名,这意味着在 2 的补码平台上您的位域将具有以下范围
- 一个 : [-4, 3]
- b : [-2, 1]
- c : [-1, 0]
您分配的值不符合这些范围,导致有符号整数溢出。赋值时有符号整数溢出的结果是实现定义的。在您的实现中,2 的补码表示显然被简单地截断为可用位数(采用最低有效位)。
例如,8
在二进制中是 1000
,在截断为 3 位后,我们得到 000
。 -6
在 2 的补码二进制中是 ...1111010
,截断后我们得到 10
,这又是 -2
。等等。