0

可能重复:
带有负值的无符号长整数
将负数分配给无符号整数?

#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++ 编译器。

4

2 回答 2

4

输出取决于格式字符的签名,而不是声明的签名。想一想:printf 无法知道 x 是声明为 int 还是 unsigned int,因为 C 中没有传递任何类型信息。所以它根据您告诉它打印的方式打印。%d 是有符号的,所以你得到一个有符号的值。使用 sc,它是一个 int,所以它是有符号的,但是你用 %u 打印它,所以它被视为无符号。

至于 si,它是无符号的,所以 5 可以放在它的 3 位中,所以它作为 5 传递给 printf 没有符号扩展它,所以 %d(或 %u)将它打印为 5。

于 2012-06-24T07:25:26.127 回答
2

似乎有两件事需要理解:

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

还有两件事有助于理解您的输出:

  • 可变参数函数参数的参数提升
  • 二进制补码表示

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

您正在使用与数据的符号不匹配的转换说明符(%d%u%d),因此您将获得未定义的行为,这取决于您的数据在内存中的实际表示方式。

二、C11标准规定:

6.3.1.3 有符号和无符号整数

  • 当整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新类型表示,则保持不变。

  • 否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。

  • 否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。

(据我所知,这里的相关细节至少从 C89 开始就是正确的。)

这告诉我们有关您的代码的几件事:

  • 当您分配-1给 ,unsigned int时,UINT_MAX + 1会添加UINT_MAX, 或429496729532 位整数。

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

所以你有未定义和实现定义的行为,但我们仍然可以尝试理解你的输出,只是为了好玩。我假设 32 位整数和二进制补码表示。

您的系统表示4294967295存储x11111111 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 位宽。

于 2012-06-24T08:14:17.477 回答