C中的位字段

时间:2015-06-24 12:23:00

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

我有以下代码:

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

此代码段打印-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

请解释为什么会这样。

5 个答案:

答案 0 :(得分:3)

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

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

0xffffffff

这会打印3(demo)。

答案 1 :(得分:3)

这与位字段无关。之所以发生这种情况,是因为C中的一个模糊规则称为默认参数促销

  

如果表示被调用函数的表达式具有类型   不包含原型,执行整数促销   每个参数,以及类型为float的参数都被提升为   双。这些被称为默认参数促销。

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

  

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

因此,在printf开始解释之前,你的位字段被整数提升为int类型。标志被保留,所以你看到的是包含值-1的两个补码int的十六进制表示。

作为旁注,我建议你根本不使用位字段,因为它们的行为指定不当而且它们完全不可移植。 See this

答案 2 :(得分:1)

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

你观察的是符号扩展2'的补充'-1'。

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

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

1 byte表示8 bits,在十六进制中,您最多可以在f中符合4 bits。因此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的表示形式的补充。没有魔力。