有符号位域表示法

6

我创建了一个大小为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?当我使用有符号整数而不是无符号整数时,它总是会成为一个负数吗?

2个回答

13
如果你期望位域类型除了可以存储 n 个比特以外还有其他值方面的含义,那么你绝不能使用纯粹的 int 作为位域类型。根据 C11 标准,无论 int 是否带符号,int 在位域中都是实现定义的,如下所示:6.7.2p5

5 每个由逗号分隔的多重集合指定相同的类型,但对于位域,由于具体实现方式不同,int 指定符号整数或无符号整数的类型是未定义的。

在您的情况下,int 指定的类型是 signed int,在 GCC 中这是默认值(见此处)

是否将“普通” int 位域视为带符号 int 位域或无符号 int 位域(C90 6.5.2、C90 6.5.2.1、C99 和 C11 6.7.2、C99 和 C11 6.7.2.1)。

默认情况下,它被视为带符号 int,但可以通过 -funsigned-bitfields 选项更改。

因此,任何合理的程序都应明确指定 signed int 或 unsigned int,具体取决于当前使用情况。

然后,有符号数字是使用补码、反码还是原码实现是由实现定义的 - 或许是补码和原码,如果是使用补码或原码,那么1位中可以存储的唯一值是符号位,因此是0;因此带符号的1位二进制字段可能只在使用2's complement时才有意义。

您的系统似乎使用2's complement - 这是例如GCC始终使用的

有符号整数类型是否使用符号和大小、二进制补码还是一的补码,以及特殊值是陷阱表示还是普通值(C99和C11 6.2.6.2)。

GCC仅支持二进制补码整数类型,所有位模式都是普通值。

因此,bit值10被解释为有符号的2's complement数字:前者设置了符号位,所以它是负数(-1),而后者没有设置符号位,所以它是非负数(0)。

因此,在一个2位的带符号位字段中,2个bit的可能模式及其在2's complement机器上的整数值为:

  • 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;


2
当你将它与位域相关的所有其他实现定义的内容叠加在一起时,它们的实用性会大大降低。 - StoryTeller - Unslander Monica
据我所知,一补数表示法极少使用。 - user1196549
@YvesDaoust,这就是为什么我的上一个版本已经提到了GCC使用的是二进制补码...因此,任何使用GCC的主机目标都将使用二进制补码。据我所知,没有比GCC支持更多体系结构的编译器。 - Antti Haapala -- Слава Україні

1
当您调用可变参数函数(如printf)时,一些参数会被 提升。例如,位域将经历 整数提升,在此过程中它将被提升为普通的int值。这种提升带来了符号扩展(因为位域的基本类型是有符号的)。这个符号扩展将使它成为-1
使用位域时,几乎总是使用无符号类型作为基础。

当使用printf调用每个位字段时,我可以期望它们都是负数吗? - Tom Slutsky
@tomslu 这取决于基本类型、它的宽度和它的值。还有编译器(根据Antti Haapala的回答)。 - Some programmer dude
“始终使用无符号类型作为基础”:为什么?如果您确实需要有符号整数,请使用有符号整数。 - user1196549
@YvesDaoust 或许我不该说“总是”,但有符号位值在位域中的使用情况非常非常少。 - Some programmer dude
我完全同意对于1位字段的使用。对于更大的尺寸,这些字段实际上经常被用作枚举值,其符号无关紧要,并希望编译器能够一致地处理。对于真正的数值位字段,谁知道呢... - user1196549

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接