我有以下代码:
#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
。
请解释为什么会这样。
答案 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
的输出看起来像ffffffff
(0xffffffff
)。
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
的表示形式的补充。没有魔力。