C语言中的无符号和有符号整数

C语言中的无符号和有符号整数,c,C,可能重复: 我不清楚“x”和“s.c”的输出(s.c只能存储3位,但在输出中它给出了非常大的值)。“x”被声明为无符号,因此存储在x中的位是1111111。。。。。。。x的输出应该是一个大值,而不是-1。第1个printf()语句按预期给出结果 我正在使用devc++编译器 输出取决于格式字符的签名,而不是声明的签名。想想看:printf不知道x是声明为int还是未签名的int,因为在C中没有传递类型信息。所以它根据您告诉它的打印方式进行打印d是有符号的,所以得到一个有符号的值。对于s.c,

可能重复:

我不清楚“x”和“s.c”的输出(s.c只能存储3位,但在输出中它给出了非常大的值)。“x”被声明为无符号,因此存储在x中的位是1111111。。。。。。。x的输出应该是一个大值,而不是-1。第1个printf()语句按预期给出结果
我正在使用devc++编译器

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


至于s.i,它是无符号的,因此5可以放入它的3位中,因此它作为5传递给printf,而不进行符号扩展,因此%d(或%u)将它打印为5。

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


至于s.i,它是无符号的,所以5可以放入它的3位,所以它作为5传递给printf,而不需要符号扩展它,所以%d(或%u)将它打印为5。

似乎有两件事需要理解:

  • printf()
    转换说明符
  • 积分转换
还有两件事有助于理解您的输出:

  • 变元函数参数的参数提升
  • 二的补语表示
首先,
printf()
是一个可变函数。它不知道参数的类型(格式字符串除外),因此必须使用转换说明符来告诉它如何解释参数。这些参数受“默认参数升级”的约束,因此您的3位字段将升级为
int
s

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

第二,C11标准规定:

6.3.1.3有符号和无符号整数

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

  • 否则,如果新类型是无符号的,则会通过重复地将新类型中可以表示的最大值加上或减去一个值来转换该值,直到该值在新类型的范围内

  • 否则,新类型已签名,且无法在其中表示值;要么结果是实现定义的,要么引发实现定义的信号

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

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

  • -1
    赋值给
    无符号整数
    时,会将
    UINT_MAX+1
    添加到该整数中,为32位整数指定
    UINT_MAX
    ,或
    4294967295

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

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

您的系统将存储在
x
中的
4294967295
表示为
11111111111111111
。当您告诉
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
向我表明,实际上您使用的是2的补码表示法(无论如何,这是一个非常安全的赌注),而
s.c
的输出表明您的
int
s是32位宽。

似乎有两件事需要理解:

  • printf()
    转换说明符
  • 积分转换
还有两件事有助于理解您的输出:

  • 变元函数参数的参数提升
  • 二的补语表示
首先,
printf()
是一个可变函数。它不知道参数的类型(格式字符串除外),因此必须使用转换说明符来告诉它如何解释参数。这些参数受“默认参数升级”的约束,因此您的3位字段将升级为
int
s

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

第二
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