带符号的位字段表示

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 intunsigned 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.

因此位值 10 是根据带符号的二进制补码数来解释的:前者设置了符号位,因此它是负数 (-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.

使用位域时,几乎总是使用无符号类型作为基础。