【问题标题】:Signed bit field represetation有符号位字段表示
【发布时间】:2017-03-01 09:02:35
【问题描述】:

我创建了一个字段大小为 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;
}

输出是: 000000000000000000000000000000001

-1

在那种情况下,为什么我的位字段的值是-1,当我使用有符号整数而不是无符号时它总是一个负数?

【问题讨论】:

    标签: c bit-fields


    【解决方案1】:

    你不应该使用普通的 int 作为位域类型,如果你期待一些关于值的东西除了它可以保存 n 位 - 根据 C11 标准它实际上是实现-定义位域中的int是有符号还是无符号6.7.2p5

    5 每个逗号分隔的多重集指定相同的类型,除了位域,说明符 int 是否指定与 signed int 相同的类型是实现定义unsigned int相同的类型。

    在您的情况下,int 指定与signed int 相同的类型;这是the default in GCC

    “普通”int 位域被视为有符号整数位域还是无符号整数位域(C90 6.5.2、C90 6.5.2.1、C99 和 C11 6.7.2、 C99 和 C11 6.7.2.1)。

    默认情况下,它被视为signed int,但这可以通过-funsigned-bitfields 选项进行更改。

    因此,任何理智的程序总是指定任一 signed intunsigned int,这取决于哪个适合当前用例。


    然后是实现定义带符号的数字是在一个补码还是二进制补码 - 或者可能是符号和幅度 - 如果它们在一个补码或 s-and-m 中,那么唯一可以存储的值1 位是符号位,因此为 0;所以一位的有符号位字段可能只对 2 的补码有意义。

    您的系统似乎使用 2 的补码 - 例如what GCC always uses:

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

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

    因此位值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 被符号扩展为全角@987654351 @; 默认参数提升也是如此;当它被传递到printf 时,该值被符号扩展为(全角)int

    因此,如果您希望从一位位域获得合理的值,请使用 unsigned bit: 1;,或者如果要将其理解为布尔标志,则使用 _Bool bit: 1;

    【讨论】:

    • 当你把它放在所有其他实现定义的位域之上时,它们的用处会大大降低
    • 据我所知,一个人的补码表示形式被大量使用。
    • @YvesDaoust 好吧,这就是为什么我之前的修订版已经提到二进制补码是 GCC 使用的......所以任何以 GCC 为目标的主机都将使用二进制补码。据我所知,没有任何编译器支持比 GCC 更多的架构。
    【解决方案2】:

    当您调用可变参数函数(如printf)时,某些参数会被提升。例如,位域经过integer promotion 提升为普通的int 值。该促销带来了符号扩展(因为您的位字段的基本类型已签名)。这个符号扩展将使它成为-1

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

    【讨论】:

    • 我可以期望 evety 位字段在使用 printf 调用时为负数吗?
    • @tomslu 取决于基本类型、宽度和值。并在编译器上(根据Antti Haapala's answer)。
    • “总是使用无符号类型作为基础”:为什么?如果确实需要有符号整数,请使用有符号整数。
    • @YvesDaoust 也许我不应该说“总是”,但是位域中带符号值的用例数量非常非常。跨度>
    • 我当然同意 1 位字段。对于较大的大小,这些字段实际上经常用作枚举值,对于这些值,符号性是无关紧要的,并且希望编译器能够始终如一地处理。对于真正的数字位域,谁知道...
    猜你喜欢
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-25
    • 2014-06-28
    相关资源
    最近更新 更多