带符号的位字段表示
Signed bit field represetation
我制作了一个字段大小为 1 位的位字段,并使用 int
而不是 unsigned
。后来当我试图检查字段的值时,我发现该值为 -1。
我使用这段代码来检查二进制表示和我的位字段的值:
#include <stdio.h>
#include <stdlib.h>
union {
struct {
int bit:1;
} field;
int rep;
} n;
int main() {
int c, k;
n.field.bit=1;
for (c = 31; c >= 0; c--)
{
k = n.rep >> c;
if (k & 1)
printf("1");
else
printf("0");
}
printf("\n %d \n", n.field.bit);
return 0;
}
输出是:
00000000000000000000000000000001
-1
在那种情况下,为什么我的位字段的值是 -1 并且当我使用 signed int 而不是 unsigned 时它总是负数?
如果你期望值 除了它可以容纳 n
位之外,你不应该使用普通的 int
作为位域类型 -根据 C11 标准,位域中的 int
是有符号还是无符号 6.7.2p5:
实际上是实现定义的
5 Each of the comma-separated multisets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier int
designates the same type as signed int
or the same type as unsigned int
.
在您的例子中,int
指定与 signed int
相同的类型;这是 the default in GCC:
Whether a “plain” int bit-field is treated as a signed int bit-field or as an unsigned int bit-field (C90 6.5.2, C90 6.5.2.1, C99 and C11 6.7.2, C99 and C11 6.7.2.1).
By default it is treated as signed int
but this may be changed by the -funsigned-bitfields
option.
因此,任何理智的程序总是指定 signed int
或 unsigned int
,具体取决于哪个适合当前用例。
然后由实现定义带符号的数字是一个补码还是两个补码 - 或者可能是符号和大小 - 如果它们是一个补码或 s-and-m,那么唯一的值可以是1位存放的是符号位,因此为0;所以一位的有符号位域可能只对 2 的补码有意义。
您的系统似乎使用 2 的补码 - 例如what GCC always uses:
Whether signed integer types are represented using sign and magnitude, two’s complement, or one’s complement, and whether the extraordinary value is a trap representation or an ordinary value (C99 and C11 6.2.6.2).
GCC supports only two’s complement integer types, and all bit patterns are ordinary values.
因此位值 1
和 0
是根据带符号的二进制补码数来解释的:前者设置了符号位,因此它是负数 (-1
) 并且后者没有设置符号位,因此它是非负数 (0
)。
因此,对于 2 位的带符号位域,可能的位模式及其在 2 的补码机上的整数值是
00
- 具有 int
值 0
01
- 具有 int
值 1
10
- 具有 int
值 -2
11
- 有 int
值 -1
在n位字段中,最小有符号数为- 2^(n - 1),最大为2^(n-1) - 1。
现在,对秩小于int
的有符号整数操作数进行运算时,首先将其转换为int
,因此值-1
是有符号的-扩展到全角int
; 默认参数提升也是如此;该值在传递给 printf
.
时被符号扩展为(全角)int
因此,如果您希望从一个位域中获得合理的值,请使用 unsigned bit: 1;
或者如果要将其理解为布尔标志,则使用 _Bool bit: 1;
当您调用可变参数函数(如 printf
)时,某些参数会 提升 。例如,位字段经历了 integer promotion,它被提升为普通的 int
值。该提升带来了 sign extension(因为您的位域基本类型已签名)。此符号扩展将使 -1
.
使用位域时,几乎总是使用无符号类型作为基础。
我制作了一个字段大小为 1 位的位字段,并使用 int
而不是 unsigned
。后来当我试图检查字段的值时,我发现该值为 -1。
我使用这段代码来检查二进制表示和我的位字段的值:
#include <stdio.h>
#include <stdlib.h>
union {
struct {
int bit:1;
} field;
int rep;
} n;
int main() {
int c, k;
n.field.bit=1;
for (c = 31; c >= 0; c--)
{
k = n.rep >> c;
if (k & 1)
printf("1");
else
printf("0");
}
printf("\n %d \n", n.field.bit);
return 0;
}
输出是: 00000000000000000000000000000001
-1
在那种情况下,为什么我的位字段的值是 -1 并且当我使用 signed int 而不是 unsigned 时它总是负数?
如果你期望值 除了它可以容纳 n
位之外,你不应该使用普通的 int
作为位域类型 -根据 C11 标准,位域中的 int
是有符号还是无符号 6.7.2p5:
5 Each of the comma-separated multisets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier
int
designates the same type assigned int
or the same type asunsigned int
.
在您的例子中,int
指定与 signed int
相同的类型;这是 the default in GCC:
Whether a “plain” int bit-field is treated as a signed int bit-field or as an unsigned int bit-field (C90 6.5.2, C90 6.5.2.1, C99 and C11 6.7.2, C99 and C11 6.7.2.1).
By default it is treated as
signed int
but this may be changed by the-funsigned-bitfields
option.
因此,任何理智的程序总是指定 signed int
或 unsigned int
,具体取决于哪个适合当前用例。
然后由实现定义带符号的数字是一个补码还是两个补码 - 或者可能是符号和大小 - 如果它们是一个补码或 s-and-m,那么唯一的值可以是1位存放的是符号位,因此为0;所以一位的有符号位域可能只对 2 的补码有意义。
您的系统似乎使用 2 的补码 - 例如what GCC always uses:
Whether signed integer types are represented using sign and magnitude, two’s complement, or one’s complement, and whether the extraordinary value is a trap representation or an ordinary value (C99 and C11 6.2.6.2).
GCC supports only two’s complement integer types, and all bit patterns are ordinary values.
因此位值 1
和 0
是根据带符号的二进制补码数来解释的:前者设置了符号位,因此它是负数 (-1
) 并且后者没有设置符号位,因此它是非负数 (0
)。
因此,对于 2 位的带符号位域,可能的位模式及其在 2 的补码机上的整数值是
00
- 具有int
值 001
- 具有int
值 110
- 具有int
值 -211
- 有int
值 -1
在n位字段中,最小有符号数为- 2^(n - 1),最大为2^(n-1) - 1。
现在,对秩小于int
的有符号整数操作数进行运算时,首先将其转换为int
,因此值-1
是有符号的-扩展到全角int
; 默认参数提升也是如此;该值在传递给 printf
.
int
因此,如果您希望从一个位域中获得合理的值,请使用 unsigned bit: 1;
或者如果要将其理解为布尔标志,则使用 _Bool bit: 1;
当您调用可变参数函数(如 printf
)时,某些参数会 提升 。例如,位字段经历了 integer promotion,它被提升为普通的 int
值。该提升带来了 sign extension(因为您的位域基本类型已签名)。此符号扩展将使 -1
.
使用位域时,几乎总是使用无符号类型作为基础。