似乎有两件事需要理解:
还有两件事有助于理解您的输出:
首先,printf()
是一个可变参数函数。它不知道其参数的类型是什么(格式字符串除外),因此您必须使用转换说明符来告诉它如何解释参数。这些参数受“默认参数提升”的约束,因此您的 3 位位字段被提升为int
s。
您正在使用与数据的符号不匹配的转换说明符(%d
、%u
和%d
),因此您将获得未定义的行为,这取决于您的数据在内存中的实际表示方式。
二、C11标准规定:
6.3.1.3 有符号和无符号整数
当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则保持不变。
否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。
(据我所知,这里的相关细节至少从 C89 开始就是正确的。)
这告诉我们有关您的代码的几件事:
当您分配-1
给 ,unsigned int
时,UINT_MAX + 1
会添加UINT_MAX
, 或4294967295
32 位整数。
当您尝试分配5
给 3 位有符号位字段时,结果是实现定义的。
所以你有未定义和实现定义的行为,但我们仍然可以尝试理解你的输出,只是为了好玩。我假设 32 位整数和二进制补码表示。
您的系统表示4294967295
存储x
为11111111 11111111 11111111 11111111
. 当您告诉printf()
您传递的参数已签名时,这些相同的位将被解释为-1
,这就是您得到的输出。
对于s.c
,您似乎得到的实现定义的行为很简单:101
表示5
按原样存储的三位。这意味着使用正确的转换说明符,printf()
应该显示s.c
为-3
.
以下是您分配的值:
s.i = 101
s.c = 101
x = 11111111 11111111 11111111 11111111
0
通过为无符号值左填充并为有符号值重复符号,将 3 位值提升为 32 位:
s.i = 00000000 00000000 00000000 00000101
s.c = 11111111 11111111 11111111 11111101
x = 11111111 11111111 11111111 11111111
其中,当解释为有符号、无符号和有符号整数时,给出:
s.i=5
s.c=4294967293
x=-1
这x=-1
向我表明您实际上正在使用二进制补码表示(无论如何,这是一个非常安全的赌注),并且输出s.c
表明您int
的 s 是 32 位宽。