2

灵感来自此博客上“当我混合有符号和无符号整数时会发生什么?”下的代码片段。我决定用几个不同的有符号和无符号整数值运行它并观察行为。

这是原始片段(略有修改,但意图仍然相同)

#include <stdio.h>

int main(void)
{
    unsigned int a = 6;
    int b = -20;

    int c = (a+b > 6);
    unsigned int d = a+b;

    printf("<%d,%u>", c, d);
}

输出:<1,4294967282>

a = 6现在,当我为和运行相同的程序时b = -1

输出:<0,5>

如果我正确理解C 语言的积分提升规则,b=-1应该提升为无符号整数,那就可以了4294967295。就像原始示例一样,我们应该得到<1,4294967301>. 但这不是我们得到的。

我能想到的这种行为的唯一原因是这些隐式类型转换发生在算术运算之后。然而,同一篇博客文章还说,首先提升有符号整数,然后进行评估,这使我的推理无效。

这种行为的真正原因是什么?以及这两个示例有何不同。


PS我知道有很多关于SO的问题与这个问题相似/相关。但是我没有遇到任何特别解决这个问题或有助于理解此类代码片段的问题。如果发现是重复的,我很乐意接受这个问题被关闭。

4

2 回答 2

2

 /*unsigned*/a+/*signed*/b

bunsigned由于通常的算术转换 (6.3.1.8)将被转换为。

转换将通过重复加或减一 来完成UINT_MAX(6.3.1.3p2),这unsigned == uint32_t意味着通过添加2^32(一次)

供参考unsigned == uint32_t

UINT_MAX+1 == 2^32 == 4294967296

对于(unsiged)a==6(signed)b==-20,你得到:

2^32-20 + 6 == 4294967282  (<= UINT_MAX)

对于(unsiged)a==6 (signed)b==-1,你得到:

2^32-1 + 6 == 4294967301 (>UINT_MAX)

现在因为这个结果大于UINT_MAX,它会回绕到5(你可以通过减去 得到UINT_MAX+1,回绕是如何定义发生的(6.3.1.3p2))。

在二进制补码体系结构中,这些规则基本上转换为琐碎的add,这意味着您也可以使用签名表示 ( 6-20==-14; 6-1==5) 进行操作,然后将结果重新解释为unsigned(在第二种情况下获得 5 似乎是一种更直接的方法,不是吗),但是了解规则仍然很好,因为在 C 中未定义有符号溢出(C!=汇编),这为您的编译器提供了很大的余地,可以将代码转换为您不希望的东西,如果您假设简单的话C 到程序集的映射。

于 2017-12-02T21:38:26.313 回答
0

所以你应该是4294967301,但这不适合 32 位无符号整数,因为最大值是2^32-1or 4294967295。所以4294967295等于0xFFFFFFFF。与0xFFFFFFFF + 60x000000052147483647当有符号整数从( 0x7FFFFFFF)翻转到-2147483648( 0x80000000) 时,称为溢出。4294967295当无符号从( 0xFFFFFFFF)翻转到0( 0x00000000) 时,它是一个环绕。

于 2017-12-02T21:23:19.180 回答