可能重复:带有负值的无符号长整数给无符号整数赋值?
#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编译器 .
2 回答
似乎有两件事需要理解:
printf()
转换说明符完整转换
还有两件事有助于理解你的输出:
可变参数函数参数的参数提升
二进制补码表示
首先,
printf()
是一个可变函数 . 它不知道它的参数类型是什么(格式字符串除外),因此您必须使用转换说明符来告诉它如何解释参数 . 这些参数受"default argument promotions"约束,因此您的3位位字段将被提升为int
.您正在使用与数据的签名不匹配的转换说明符(
%d
,%u
和%d
),因此您将获得未定义的行为,这取决于您的数据在内存中的实际表示方式 .其次,C11标准规定:
(据我所知,至少自C89以来,此处的相关细节都是真实的 . )
这告诉我们有关您代码的一些事情:
将
-1
分配给unsigned int
时,会向其添加UINT_MAX + 1
,为32位整数提供UINT_MAX
或4294967295
.当您尝试将
5
分配给3位有符号位字段时,结果是实现定义的 .所以你've got both undefined and implementation-defined behavior, but we can still try to make sense of your output, just for fun. I'm假设32位整数和two's complement表示 .
您的系统将
4294967295
中存储的4294967295
表示为11111111 11111111 11111111 11111111
. 当您告诉printf()
您传递的参数已经签名时,那些相同的位被解释为-1
,这是您得到的输出 .对于
s.c
,您似乎已经获得的实现定义的行为很简单:表示5
的三位101
已按原样存储 . 这意味着使用正确的转换说明符,printf()
应将s.c
显示为-3
.以下是您分配的值:
对于无符号值,使用
0
左边填充将3位值提升为32位,并重复签名值的符号:当解释为有符号,无符号和有符号整数时,它给出:
x=-1
向我建议你实际上使用二进制补码表示(无论如何这是一个非常安全的赌注),s.c
的输出表明你的int
是32位宽 .输出取决于格式字符的签名,而不是声明的签名 . 想一想:printf无法知道x是否被声明为int或unsigned int,因为在C中没有传递类型信息 . 所以它根据你告诉它打印的方式进行打印 . %d已签名,因此您将获得签名值 . 使用s.c,它是一个int,所以它已经签名,但你用%u打印它,所以它被视为无符号 .
至于s.i,它是无符号的,所以5可以适合它的3位,所以它被传递给printf为5而没有符号扩展它,所以%d(或%u)将它打印为5 .