C通常的算术转换

我正在阅读关于通常算术转换的C99标准。

如果两个操作数具有相同的类型,则不需要进一步转换。

否则,如果两个操作数都具有有符号整数类型或两者都具有无符号整数类型,则具有较小整数转换等级类型的操作数将转换为具有更高等级的操作数的类型。

否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的等级,则具有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型。

否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数将转换为带有符号整数类型的操作数的类型。

否则,两个操作数都转换为无符号整数类型,对应于带有符号整数类型的操作数的类型。

所以我要说我有以下代码:

#include  int main() { unsigned int a = 10; signed int b = -5; printf("%d\n", a + b); /* 5 */ printf("%u\n", a + b); /* 5 */ return 0; } 

我认为粗体段落适用(因为unsigned intsigned int具有相同的等级。为什么不将b转换为unsigned ?或者它可能转换为unsigned但是有些东西我不明白?

感谢您的时间 :-)

只要只使用32位, 0x0000000a0xfffffffb将始终为0x00000005 ,无论您是处理有符号还是无符号类型。

实际上b 转换为无符号。 然而,您观察到的是b转换为无符号然后添加到10赋予值5。

在x86 32bit上就是这样

  1. b ,转换为无符号,变为4294967291 (即2**32 - 5
  2. 因为在2**32处环绕而增加10变为5( 2**32 - 5 + 10 = 2**32 + 5 = 5

从问题中重复代码的相关部分:

 unsigned int a = 10; signed int b = -5; printf("%d\n", a + b); /* 5 */ printf("%u\n", a + b); /* 5 */ 

a + bb被转换为unsigned int,(由无符号到符号转换的规则产生UINT_MAX + 1 - 5 )。 根据无符号算术的规则,将10加到该值的结果为5,其类型为unsigned int。 在大多数情况下,C表达式的类型与其出现的上下文无关。 (请注意,这些都不取决于表示;转换和算术完全是根据数值定义的。)

对于第二次printf调用,结果很简单: "%u"需要一个unsigned int类型的参数,并且你给它一个。 它打印"5\n"

第一个printf有点复杂。 "%d"需要一个int类型的参数,但是你给它一个unsigned int类型的参数。 在大多数情况下,这样的类型不匹配会导致未定义的行为,但是有一个特殊情况规则,相应的有符号和无符号类型可以作为函数参数互换 – 只要该值在两种类型中都是可表示的(就像在这里一样) 。 所以第一个printf也打印"5\n"

同样,所有这些行为都是根据值而不是表示来定义的(除了要求给定值在相应的有符号和无符号类型中具有相同的表示)。 你会在一个系统上得到相同的结果,其中signed int和unsigned int都是37位,signed int有7个填充位,unsigned int有11个填充位,而signed int使用1s’补码或符号和幅度表示。 (据我所知,现实生活中不存在这样的系统。)

它被转换为unsigned ,无符号算术恰好会给出你看到的结果。 无符号算术的结果相当于使用二进制补码进行带符号算术,并且没有超出范围的exception。