问题 两个无符号短路的乘法是否真的会导致未定义的行为?


我花了一些时间在这个网站上搜寻;特别是这个问题: ((a +(b&255))&255)是否与((a + b)&255)相同?

在这样做的过程中,我得出了结论

int main()
{
    unsigned short i = std::numeric_limits<unsigned short>::max();
    unsigned short j = i;
    auto y = i * j;
}

由于类型的提升,可能会导致未定义的行为 i 和 j 至 int 随后在乘法时溢出!也许 i 和 j 甚至不需要这么大。

我的结论是,例如,在一个系统中 unsigned short 是16位和 int 是32位,行为可以 未定义

我在这里纠正吗?


10509
2017-11-24 11:45


起源

不,它是定义的行为(女巫变得与...相同 (i*j)%std::numeric_limits<unsigned short>::max())不考虑溢出 - apple apple
@apple没有,这是不正确的。 - Yakk - Adam Nevraumont
遗憾的是你无法发表评论。 - Fitzwilliam Bennet-Darcy
@Yakk @ FitzwilliamBennet-Darcy你能解释一下为什么它是未定义的吗?我不认为 i 和 j 适合 prvalue 作为@krzaq的报价 - apple apple
@appleapple他们将首先进行左值到右值的转换。 - TartanLlama


答案:


是的,这是可能的,并且您的示例可能在大多数桌面架构上未定义。

为了这个例子,让我们假设 int 是32位2的补码类型 unsigned short 是16位。

我正在使用N4140作为报价。

在乘法之前,两个值都被提升为 int

§4.5[conv.prom] / 1

除bool,char16_t,char32_t或之外的整数类型的prvalue   wchar_t,其整数转换等级(4.13)小于等级   如果int可以表示all,则int可以转换为int类型的prvalue   源类型的值;

然后:

§5[expr] / 4

如果在评估表达式期间,结果不是   在数学上定义或不在可表示值的范围内   它的类型,行为是未定义的。

由于65535 * 65535(4294836225)的结果未在我们的32位中定义 int (值范围[-2147483648,2147483647]),行为未定义。


12
2017-11-24 11:55



它在我的PC上溢出,上面的例子是用VS 2013编译的。
@RawN谢谢,编辑。 - krzaq
很棒的答案!我认为推广规则是C语言中最糟糕的事情之一(在语言设计方面),不幸的是C ++采用了这种规则。它们是无数微妙错误的根源。幸运的是,编译器可以在某个时候发出警告。 - vsoftco
不会将表达式的类型提升为 unsigned int 如果值不能适合int? - 0x499602D2
@ 0x499602D2不,我引用相关段落 - krzaq