首页 文章

C中的无符号和有符号整数[重复]

提问于
浏览
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编译器 .

2 回答

  • 2

    似乎有两件事需要理解:

    • printf() 转换说明符

    • 完整转换

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

    • 可变参数函数参数的参数提升

    • 二进制补码表示

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

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

    其次,C11标准规定:

    6.3.1.3有符号和无符号整数当整数类型的值转换为_Bool以外的另一个整数类型时,如果该值可以用新类型表示,则它不变 . 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内 . 否则,新类型将被签名,并且值无法在其中表示;结果是实现定义的,或者引发实现定义的信号 .

    (据我所知,至少自C89以来,此处的相关细节都是真实的 . )

    这告诉我们有关您代码的一些事情:

    • -1 分配给 unsigned int 时,会向其添加 UINT_MAX + 1 ,为32位整数提供 UINT_MAX4294967295 .

    • 当您尝试将 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 .

    以下是您分配的值:

    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 是32位宽 .

  • 4

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

    至于s.i,它是无符号的,所以5可以适合它的3位,所以它被传递给printf为5而没有符号扩展它,所以%d(或%u)将它打印为5 .

相关问题