当R.H.S在算术运算中的int范围之外有负int和unsigned int

时间:2018-06-02 13:11:37

标签: c unsigned-integer integer-arithmetic

我为标题道歉,因为我不得不以某种方式找到一个独特的标题。

请考虑以下代码:

#include<stdio.h>
int main(void)
{
  int b = 2147483648; // To show the maximum value of int type here is 2147483647
  printf("%d\n",b);

  unsigned int a = 2147483650;
  unsigned int c = a+(-1);
  printf("%u\n",c);
}

使用gcc编译器在64位操作系统上运行时,上述程序的输出为:

-2147483648
 2147483649

请参阅我对此案的理解:

Unsigned int a超出了signed int类型的范围。在R.H.S(-1)中将转换为unsigned int,因为操作数是不同类型的。将-1转换为unsigned int的结果是:

-1 + (unsigned int MAX_UINT +1) = unsigned int MAX_UINT = 4294967295.

现在R.H.S将会:

unsigned int MAX_UINT + 2147483650

现在看起来它超出了unsigned int的范围。我不知道如何从这里开始,看起来即使我继续这个解释,我也不会达到经验输出。

请给出正确的解释。

PS:知道int b = 2147483648成为-2147483648的方式不是我的意图。我刚刚在代码中添加了该行,因此很清楚2147483650 超出int范围。

3 个答案:

答案 0 :(得分:3)

2147483648不是32位int,而是高于INT_MAX,在此类平台上的值为2147483647

int b = 2147483648;是实现定义的。在您的平台上,它似乎执行32位环绕,这是两种补充架构的典型,但不受C标准的保证。

因此printf("%d\n", b);输出-2147483648

其余代码在32位系统上完美定义,输出2147483649是正确且预期的。操作系统 64 bit 在评估步骤中起着非常微妙的作用,但实际上与C标准完全无关的实际结果无关。

以下是步骤:

  • unsigned int a = 2147483650;这里不足为奇,aunsigned int,其初始值设定项为intlong int或{{1取决于这些类型中的哪一个具有至少32个值位。在Windows和32位Linux上,它将是long long int,而在64位Linux上,它将是long long int。存储到long int变量时,该值将被截断为32位。

    您可以通过添加以下代码来验证这些步骤:

    unsigned int
  • 第二个定义printf("sizeof(2147483650) -> %d\n", (int)sizeof(2147483650)); printf(" sizeof(a) -> %d\n", (int)sizeof(a)); 经历了相同的步骤:

    • unsigned int c = a+(-1);被定义为c,当存储到unsigned int时,其初始化程序被截断为32位。初始化程序是一个补充:
    • 第一个字词是c,其值为unsigned int
    • 第二个术语是带括号的表达式,其中2147483650U的一元否定值为int。因此,正确分析后,它是1,其值为int
    • 第二项转换为-1:转换以2 32 为模,因此值为unsigned int
    • 然后使用无符号算术执行加法,该算法被指定为以4294967295U类型的宽度为模,因此结果为unsigned int,其值为unsigned int,({ {1}} modulo 2 32
    • 2147483649U值存储在6442450945中,并使用unsigned int作为c正确打印。

如果表达式是printf("%u\n", c);,则计算将在64位带符号的算术中进行,类型为21474836492147483650 + (-1),具体取决于体系结构,结果为{ {1}}。当存储到long int时,该值将被截断为32位,因此long long int的值与2147483649相同。

请注意,上述步骤不依赖于负值的实际表示。它们是针对所有体系结构完全定义的,只有类型c的宽度很重要。

您可以使用额外代码验证这些步骤。这是一个完整的仪表程序来说明这些步骤:

c

输出:

2147483649

答案 1 :(得分:1)

int b = 2147483648;
printf("%d\n",b);
// -2147483648

转换一个超出目标签名类型范围的整数(任何有符号或无符号):

  

...结果是实现定义的,或者引发实现定义的信号。 C11§6.3.1.33

对于带符号整数2147483648的情况,实现定义的行为似乎将源2147483648的最低32位映射到int的32位。这可能不是另一个编译器的结果。

a+(-1)a + (-(1u))相同,a + (-1u + UINT_MAX + 1u)a + UINT_MAX相同。 添加溢出unsigned范围,但无符号溢出包围。因此,在分配之前,总和为2147483649 。使用以下代码,不会超出范围转换。唯一的转化是signed 1unsigned 1long 2147483650(或long long 2147483650)到unsigned 2147483650。两者都在范围转换中。

unsigned int a = 2147483650;
unsigned int c = a+(-1);
printf("%u\n",c);
//  2147483649

答案 2 :(得分:0)

像这样看

0xFFFFFFFF

0来自哪里?好吧,0x000000001,如果从中减去0xFFFFFFFF,则得{4},因为无符号算术被定义为“换行”。

或者进一步采用十进制版本,0 - 1UINT_MAX,因为unsigned int包装,总和也是如此。

your value      2147483650
UINT_MAX      + 4294967295
                ----------
                6442450945
modulo 2^32   % 4294967296
                ----------
                2147483649