两个无符号短整型相乘是否真的会导致未定义行为?

11

我花了一些时间在这个网站上搜索,特别是这个问题:((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;
}

由于将ij提升为int类型而导致的乘法溢出可能会导致未定义的行为!也许ij甚至不需要那么大。

我的结论是,例如,在一个unsigned short为16位且int为32位的系统上,行为可能是未定义的。

我这里说得对吗?


1
不,这是定义行为(它将成为与“(i * j)%std :: numeric_limits <unsigned short> :: max()”相同的值),而不考虑溢出。 - apple apple
4
@苹果 不,那不是正确的。 - Yakk - Adam Nevraumont
2
很遗憾,您无法对评论进行踩。 - Fitzwilliam Bennet-Darcy
@Yakk @FitzwilliamBennet-Darcy,你们能解释一下为什么它是未定义的吗?我不认为ij适合于prvalue,就像@krzaq引用的那样。 - apple apple
2
@appleapple 首先它们将经历lvalue-to-rvalue转换。 - TartanLlama
那么接下来的问题就是:我们如何避免这里出现溢出?有没有一种方法可以强制编译器在整个计算过程中保持“unsigned short”类型呢? - vsoftco
1个回答

12

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

为了举例说明,假设int是32位2进制补码类型,unsigned short是16位。

我在引文中使用N4140。

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

§ 4.5 [conv.prom] / 1

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

然后:

§ 5 [expr] / 4

如果在表达式的评估过程中,结果在数学上未定义或不在其类型的可表示值范围内,则行为未定义。

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


1
在我的PC上,使用VS 2013编译上述示例时会发生溢出。 - user1593881
很棒的回答!我认为在语言设计方面,C++中不幸采用了最糟糕的东西之一——推广规则。它们是无数微妙错误的根源。幸运的是,编译器有时可以发出警告。 - vsoftco
如果值无法适应int类型,表达式的类型会提升为“unsigned int”吗? - David G
@0x499602D2 不,我引用了相关段落。 - krzaq
如果促销改变了值(这实际上是唯一的问题),您可以像这里解释的那样使用-Wconversion:https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html - Thomas Legris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接