是否从C ++未定义的行为中减去较大的无符号值?

时间:2015-02-28 08:42:50

标签: c++ unsigned signed subtraction

在“完整促销”和“通常的算术转换”之后的下面的程序中,运算符-的两个操作数都保持unsigned long long。之后,C ++ 11标准说:

  

5.7.3二元运算符的结果是第二个操作数从第一个操作数减去所产生的差异。

标准是否更详细地定义了减法的精确程度(或者是指定义减法的其他文档)?

从较小的无符号整数中减去较大的无符号整数会产生未定义的行为吗?为什么?

在下面的示例程序中执行赋值c=a-b可以保证c在所有(甚至理论上)可能符合C ++ 11标准的机器架构上-3以及为什么?

int main()
{
    unsigned long long a=2, b=5;
    signed long long c=a-b;
}

2 个答案:

答案 0 :(得分:6)

无符号值的减法由(3.9.1)[basic.fundamental] / 4明确定义:

  

无符号整数,声明为unsigned,应遵守算术模2 n 的定律,其中 n 是位数在该特定大小的整数的值表示中。 46

     

46)这意味着无符号算术不会溢出,因为无法用结果无符号整数类型表示的结果是以比可以表示的最大值大1的数量为模的方式减少的。由结果无符号整数类型。

但是,分配会导致c具有实现定义的值(也就是说,您的里程可能会有所不同)。关于赋值运算符,(5.17)[expr.ass] / 3必须说

  

如果左操作数不是类类型,则表达式被隐式转换(第4节)到左操作数的cv-unqualified类型。

第4条([转])在(4.7)[conv.integral] / 3

中说
  

如果目标类型已签名,则该值如果可以在目标类型(和位字段宽度)中表示,则不会更改; 否则,该值是实现定义的。

重申:a - b定义明确,c = a - b不是因为a - b的结果无法代表c

历史原因是,虽然今天几乎所有的计算机都使用二进制补码表示有符号整数,但在过去的日子里,有些机器使用了other representations(特别是一个补码和有符号量)但没有与两个补码相同的值范围。如果用二进制补码表示法自然地定义了无符号到符号的转换,那么在这样的机器上实现C ++是不可能的(或者至少是非常困难的),并且如果用其中一种表示法自然地定义它,我们今天有一个更大的问题。

答案 1 :(得分:-1)

a - b的结果是明确定义的,因为如果我没记错的话,C ++保证了它们的双补语义。但是,由此产生的underfklow将超出signed long long的范围,因此将此值转换为带符号的long long将是未定义的行为。