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



在下面的程序中,在"积分提升"one_answers"常用算术转换"之后,运算符-的两个操作数都保持为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;
}

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

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

46)这意味着无符号算术不会溢出,因为不能由结果的无符号整数类型表示的结果被模减为比结果的无签名整数类型所能表示的最大值大一的数字。

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

如果左操作数不是类类型,则表达式将隐式转换(第4条)为左操作数的cv非限定类型。

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

如果目的地类型是有符号的,那么如果可以用目的地类型(和位字段宽度)表示,则该值不变否则,值由实现定义

重申一下:a - b是明确定义的,c = a - b不是因为a - b的结果不能用c表示。

历史上的原因是,虽然今天几乎所有的计算机都使用二的补码表示来表示有符号整数,但在过去,也有机器使用其他表示(尤其是一的补码和有符号幅度),这些表示的值范围与二的补码不同。如果无符号到有符号的转换是根据二的补码表示的自然条件定义的,C++将不可能(或至少非常困难)在这样的机器上实现,如果它是根据其中一种表示的自然方式定义的,我们今天会遇到更大的问题。

a - b的结果定义良好,因为如果我回忆正确的话,C++保证了它们的二补码语义。然而,所产生的欠电流将在signed long long的范围之外,因此将该值转换为带符号的long long将是未定义的行为。

相关内容

  • 没有找到相关文章

最新更新