编译器如何处理无符号整数的减法?

时间:2018-03-26 18:46:31

标签: c

我无法理解两个无符号整数之间的简单算术运算;为什么结果为“802”? 假设整数数字大小为32 - 由编译器保证的位。

#include <stdio.h>
#include <stdint.h>

int main(int argc, char const *argv[])
{
    uint32_t Temp1;
    Temp1 = (uint32_t)801 - (uint32_t)0xFFFFFFFF; // Two Unsigned Integers

    uint32_t Temp2;
    Temp2 = 801 - 0xFFFFFFFF;// Two Signed Integers and result assigned to unsigned


    unsigned int Temp3;
    Temp3 = (unsigned int)((unsigned int)801 - (unsigned int)0xFFFFFFFF); // Two Unsigned Integers

    int32_t Temp4;
    Temp4 = 801 - 0xFFFFFFFF;// Two Signed Integers

    printf("Temp1: %d %u\n",Temp1,Temp1 );
    printf("Temp2: %d %u\n",Temp2,Temp2 );
    printf("Temp3: %d %u\n",Temp3,Temp3 );
    printf("Temp4: %d %u\n",Temp4,Temp4 );  
    return 0;
}

输出:

Temp1: 802 802
Temp2: 802 802
Temp3: 802 802
Temp4: 802 802

如果我将表达式置于if条件下,编译器将如何处理它?它会将表达式((uint32_t)OPERAND_1 - (uint32_t)0xFFFFFFFF)转换为有符号整数还是无符号整数,然后进行求值?

if(((uint32_t)801 - (uint32_t)0xFFFFFFFF) > (uint16_t)1500)
{
    printf("Hello All\n");
}
else
{
    printf("Hello None\n");
}

4 个答案:

答案 0 :(得分:4)

假设0xFFFFFFFF为32位,十六进制常量int的类型为unsigned int。因此,当在801的表达式中使用int时,由于通常的算术转换unsigned int类型>

以上示例中的任何一点都没有对签名类型进行算术运算,因此没有关于签名环绕或负数表示的实现定义行为。

C standard的第6.3.1.8节说明了关于整数类型转换的以下内容:

  

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

     

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

     

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

     

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

     

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

第三段是在这种情况下生效的段落,因为intunsigned int具有相同的等级。这意味着上面的所有算术都是在unsigned int类型上完成的。

这种类型的操作发生模2 32 ,这实际上意味着值环绕。从0xFFFFFFFF中减去801包围到导致802,这是在所有情况下都会被打印的内容。

为了详细说明环绕行为,我们以无符号3位整数类型为例,其值为0到7.假设我们用这种类型计算3 - 7。这类似于您正在进行的减法,因为减去的值是数据类型可以容纳的最大值。

现在我们减去:

  • 3 - 1 = 2
  • 3 - 2 = 1
  • 3 - 3 = 0
  • 3 - 4 = 7
  • 3 - 5 = 6
  • 3 - 6 = 5
  • 3 - 7 = 4

注意一旦第二个操作数大于第一个操作数,值如何包围,并且该有效结果与添加1相同。这与示例中的相同。

答案 1 :(得分:2)

在C中,无符号算术以模2 ^ n完成,其中n是无符号整数的位数。

答案 2 :(得分:1)

大多数情况下,计算机使用所谓的2补码来表示有符号整数。对于4个字节的int,0xFFFFFFFF-1。  Two's complement - Wikipedia

#include <stdio.h>
#include <stdint.h>

int main(int argc, char const *argv[])
{
    uint32_t Temp1;
    Temp1 = (uint32_t)801 - (uint32_t)0xFFFFFFFF; // Two Unsigned Integers

    uint32_t Temp2;
    Temp2 = 801 - 0xFFFFFFFF; // Two Signed Integers and result assigned to unsigned

    unsigned int Temp3;
    Temp3 = (unsigned int)((unsigned int)801 - (unsigned int)0xFFFFFFFF); // Two Unsigned Integers

    int32_t Temp4;
    Temp4 = 801 - 0xFFFFFFFF; // Two Signed Integers


    printf("Temp1: %d %X %u %d\n",Temp1,Temp1, Temp1, 0xFFFFFFFF );
    printf("Temp2: %d %X %u %X\n",Temp2,Temp2, Temp2, 0xFFFFFFFF );
    printf("Temp3: %d %X %u %u\n",Temp3,Temp3, Temp3, 0xFFFFFFFF );
    printf("Temp4: %d %X %u %x\n",Temp4,Temp4, Temp4, 0xFFFFFFFF );
    return 0;
}

输出:

Temp1: 802 322 802 -1                                                                                                                          
Temp2: 802 322 802 FFFFFFFF                                                                                                                    
Temp3: 802 322 802 4294967295                                                                                                                  
Temp4: 802 322 802 ffffffff 

答案 3 :(得分:0)

在时钟算术中,如果您将一组数字加到模数减去1,则净效果与添加1相同。另一方面,将模数减1减去数字会产生加1的效果。如果你减去11h到06h你就会得到07h的时钟。在这种情况下,您将2^32 - 1减去801,这应该是负数。为了保留无符号,您必须在结果中添加2^32,以使数字符合0..2^32-1范围,即801 - (2^32 - 1) + 2^32 = 801 - 2^32 + 2^32 -(-1) = 802