【问题标题】:Bit Fields in CC中的位域
【发布时间】:2023-03-10 15:00:01
【问题描述】:

我有以下代码:

#include <stdio.h>
struct test 
{
   int x: 2;
   int y: 2;
};
int main()
{
   test t;
   t.x = -1;
   printf("%d", t.x);
   return 0;
}

这个sn-p打印-1我能理解,

如果代码相同,%d被替换为%x格式说明符,如下所示:

#include <stdio.h>
struct test 
{
   int x: 2;
   int y: 2;
};
int main()
{
   test t;
   t.x = -1;
   printf("%x", t.x);
   return 0;
}

输出变为ffffffff

请解释为什么会这样。

【问题讨论】:

  • 两个 sn-ps 都相同.....(已编辑)不再。 :-)
  • 你认为-1的表示是什么?
  • 位域无关紧要 - 如果将 -1 传递给 printf 代替 t.x,您将得到相同的结果。
  • 我不明白为什么要投反对票?
  • @rakeb.void 某人有一个“愤怒的一天”。我写了唯一的答案来解释 为什么 位字段变成一个 int 并且也被否决了,没有给出任何理由......

标签: c int hex bit-fields negative-number


【解决方案1】:

这与位域本身无关。发生这种情况是因为 C 中有一条晦涩的规则,称为 默认参数提升

如果表示被调用函数的表达式有一个类型 不包括原型,整数提升在 每个参数,并且具有浮点类型的参数被提升为 双倍的。这些称为默认参数提升。

(强调我的)而且这条规则也适用于像 printf 这样的可变参数函数:

函数原型声明器中的省略号表示 参数类型转换在最后一个声明的参数之后停止。 默认参数提升是在尾随参数上执行的。

因此,在 printf 甚至开始解释它之前,您的位字段已被提升为类型 int 的整数。该符号被保留,因此您看到的是二进制补码 int 的十六进制表示,其中包含值 -1。

附带说明一下,我不建议您使用位字段,因为它们的行为没有明确规定,而且它们完全不可移植。 See this.

【讨论】:

    【解决方案2】:

    %x 表示printf 将以十六进制格式输出其值。现在,问题是为什么-1 的输出看起来像ffffffff (0xffffffff)。

    1 byte 表示8 bits,在十六进制中,4 bits 中最多可以容纳f。所以1 byte 可以保存十六进制的ff 的最大值。

    所以,

    Bytes       Hex            Bin
    1 byte      ff             11111111
    4 byte      ff ff ff ff    11111111 11111111 11111111 11111111
    

    现在,-1 代表Two's complement,表示它是1 的负数。

    toggle:   00000000 00000000 00000000 00000001 (to fit in 4 byte)
    toggle:   11111111 11111111 11111111 11111110
    +1    :                                    +1
    ----------------------------------------------
    value :   11111111 11111111 11111111 11111111
    hex   :   ff       ff       ff       ff
    

    所以,0xffffffff-1 的补充表示。没有魔法。

    【讨论】:

      【解决方案3】:

      %x 打印给定参数值的十六进制表示。 -1 的二进制补码表示,以十六进制表示,为您提供 ffffffff

      FWIW:此结果与此处使用位文件变量无关,因为printf()variadic function

      【讨论】:

        【解决方案4】:

        当您将位字段传递给printf(一个具有可变数量参数的函数)时,该值将转换为int。尽管该值只有两位,但其二进制值11 在二进制补码表示中表示-1,因此该值在发送到printf 时被符号扩展为整数-1。这是 0xffffffff 在 32 位系统上的十六进制表示。

        如果您只想看到两个位打印为十六进制,请将您的位字段设为无符号。这将确保在打印之前不会执行符号扩展:

        #include <stdio.h>
        struct test 
        {
           unsigned int x: 2;
           unsigned int y: 2;
        };
        int main()
        {
           struct test t;
           t.x = -1;
           printf("%x", t.x);
           return 0;
        }
        

        这会打印 3 (demo)。

        【讨论】:

          【解决方案5】:

          您的系统(以及几乎所有)系统以 2 的补码形式存储负数。 -1 以 8 位 2 的补码形式表示为 FFH

          您观察到的是符号扩展 2 的“-1”补码。

          【讨论】:

            猜你喜欢
            • 2010-09-06
            • 1970-01-01
            • 1970-01-01
            • 2010-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多