混合有符号整数和无符号整数时的不合理行为

时间:2017-12-02 21:15:29

标签: c unsigned-integer integer-promotion signed-integer

下的blog下的代码片段中受到激励“当我混合使用有符号和无符号整数时会发生什么?”我决定使用几个不同的有符号和无符号整数值来运行它观察行为。

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

#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);
}
  

输出:&lt; 1,4294967282&gt;

现在我为a = 6b = -1

运行相同的程序
  

输出:&lt; 0,5&gt;

如果我正确理解Integral Promotion C语言规则,那么b=-1应该已经提升为无符号整数,这将使它成为4294967295。就像原始示例一样,我们应该得到<1,4294967301>。但这不是我们得到的。

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

这种行为的真正原因是什么?以及这两个例子如何相互不同。

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

2 个答案:

答案 0 :(得分:2)

 /*unsigned*/a+/*signed*/b
由于usual arithmetic conversions (6.3.1.8)

b将转换为unsigned

转换将由repeatedly adding or subtracting one more than 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!= assembly),它为你的编译器提供了很大的余地,可以将代码转换成你不期望的东西,如果你假设直接的C到汇编映射。

答案 1 :(得分:0)

所以你期望4294967301,但这不适合32位无符号整数,因为最大值是2^32-14294967295。因此4294967295等于0xFFFFFFFF0xFFFFFFFF + 60x00000005。当有符号整数从21474836470x7FFFFFFF)翻转到-21474836480x80000000)时,称为溢出。如果未签名的人从42949672950xFFFFFFFF)翻转到00x00000000),那么这就是一个回合。