2012-06-24 99 views
0

可能重复:
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++编译器。

+3

你为什么不缩进你的代码? –

+0

所有的参数都以'int'的形式传递给'printf',由您来设置合适的格式说明符。 –

回答

2

好像有两件事情会在需要理解:

  • printf()转换说明
  • 积分转换

而且两件事个在帮助你的输出感:

  • 参数晋升为可变参数函数的参数
  • 补码表示

首先,printf()是一个可变参数函数。它不知道它的参数是什么类型(除了格式字符串),所以你必须使用转换说明符来告诉它如何解释参数。这些参数受“默认参数促销”的影响,因此您的3位位域被提升为ints。

您正在使用转换符(%d%u%d)不匹配数据的符号性,所以你会得到取决于如何你的数据在内存中其实是代表未定义的行为。

二,C11标准规定:

6.3.1。3个符号和无符号整数

  • 当与整数类型的值被转换为比其它_Bool另一个整数类型,如果该值可以通过新的类型来表示,它是不变的。否则,如果新类型是无符号的,则通过反复地增加或减去比新类型中可表示的最大值多一个值,直到该值处于新类型的范围内来转换该值。

  • 否则,新类型被签名并且其值不能被表示;结果是实现定义的或实现定义的信号被引发。

(据我所知道的,细节相关的至少从C89这里是真实的。)

这告诉我们你的代码有两件事情:

  • 当您将-1分配给unsigned int时,将UINT_MAX + 1添加到它,给出3239位整数的UINT_MAX4294967295

  • 当您尝试将5分配给3位有符号位域时,结果是实现定义的。

所以,你有两个不确定和实现定义的行为,但我们仍然可以尝试让你的输出感,只是为了好玩。我假设32位整数和two's complement表示。

你的系统是存储在x429496729511111111 11111111 11111111 11111111。当你告诉printf()你通过它的参数被签名时,那些相同的位被解释为-1,这是你得到的输出。

对于s.c,你似乎已经得到了实现定义很简单: 的三位代表101得到5原样保存。这意味着使用正确的转换说明符,printf()应显示s.c-3

这里是您指定的值:

s.i = 101 
s.c = 101 
    x = 11111111 11111111 11111111 11111111 

3位值由左填充用0提升到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的输出表明您的32位宽。

+1

它是一个很好的解释。我有一个小小的疑问来澄清。如果sc = 3(011)的值,那么它将被转换为sc = 00000000 00000000 00000000 00000011为32位整数。由于sc是带符号的,并且其值5(101)表示它为负数,因此当转换为32位整数时,除了最后三个比特位置之外,在所有比特位置中都有1(在这种情况下,最后三个比特本身就是数字)。 – sourabh912

+0

@ sourabh912是的,你有它。请注意,111 ... 101实际上是-3。 (所有这些都假设二进制补码算术,这几乎是通用的。) –

+0

感谢Jim和Darshan。你们两位都非常有帮助。 – sourabh912

4

输出取决于格式字符的符号性,而不是声明的符号性。仔细想想:printf无法知道x是否被声明为int或unsigned int,因为C中没有传入类型信息。因此,它会根据打印的方式进行打印。 %d是有符号的,所以你得到一个有符号的值。用s.c,它是一个int,所以它被签名,但是你用%u打印它,所以它被视为无符号。

至于SI,它是无符号的,所以5可以装配在其3位,所以它被传递给printf 5无符号延伸,所以%d(或%U)打印它作为5.

+0

如果你说的是真的,那么不应该输出printf(“s.i =%d \ n”,s.i); – sourabh912

+0

你没有完成你的想法,但我在我的编辑中回答。关于它没有“如果” - 这是所有C程序员都能理解的基本问题,他们不仅仅是像你一样开始。 –

+0

对不起,我的互联网连接有问题,所以不能编辑我的评论。其实我是问,不应该输出printf(“s.i =%d \ n”,s.i);是-3(因为我的格式说明符是%d,“i”仅存储在3个位中,因此应将5(101)视为负数)。 – sourabh912