可能重复:
Unsigned long with negative value
Assigning negative numbers to an unsigned int?
#include<stdio.h>
int main()
{
struct a
{
unsigned int i:3;
int c:3;
}s;
s.i=5;
s.c=5;
printf("s.i=%d\n",s.i);
printf("s.c=%u\n",s.c);
unsigned int x = -1;
printf(" x = %d", x);
return 0;
}
输出:
s.i=5
s.c=4294967293
x=-1
我不清楚“x”和“sc”的输出(sc只能存储3位数,但在输出中它给出非常大的值)。“x”被声明为无符号所以存储的位在x中是1111111 .......并且x的输出应该是一个大值而不是-1。第一个printf()语句按预期给出结果 我正在使用devc ++编译器。
答案 0 :(得分:4)
输出取决于格式字符的签名,而不是声明的签名。想一想:printf无法知道x是否被声明为int或unsigned int,因为在C中没有传递类型信息。因此它根据您告诉它打印的方式进行打印。 %d已签名,因此您将获得签名值。使用s.c,它是一个int,所以它已经签名,但你用%u打印它,所以它被视为无符号。
至于s.i,它是无符号的,所以5可以适合它的3位,所以它被传递给printf为5而没有符号扩展它,所以%d(或%u)将它打印为5。
答案 1 :(得分:2)
似乎有两件事需要理解:
printf()
转换说明符另外两件有助于理解你的输出的东西:
首先,printf()
是一个可变函数。它不知道它的参数类型是什么(格式字符串除外),因此您必须使用转换说明符来告诉它如何解释参数。这些参数受“默认参数提升”的约束,因此您的3位位字段将被提升为int
。
您正在使用与数据签名不匹配的转化说明符(%d
,%u
和%d
),因此您将获得取决于数据方式的未定义行为实际上是在记忆中表现出来的。
其次,C11标准规定:
6.3.1.3 有符号和无符号整数
当整数类型的值转换为_Bool以外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。
否则,新类型已签名且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号。
(据我所知,至少自C89以来,此处的相关细节都是真实的。)
这告诉我们有关您代码的一些事情:
当您将-1
分配给unsigned int
时,会向其添加UINT_MAX + 1
,为{32}整数添加UINT_MAX
或4294967295
当您尝试将5
分配给3位有符号位字段时,结果是实现定义的。
所以你有未定义和实现定义的行为,但我们仍然可以尝试理解你的输出,只是为了好玩。我假设32位整数和two's complement表示。
您的系统将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位宽。