哪种方法可以在乘法上测试有符号整数溢出?

dav*_*pfx 5 c c++ language-lawyer

如果以下任何一项符合标准的方式做"正确的事"?你可以假设m,并n有型int(有符号整数).主要问题是有符号整数溢出.

样品1.

size_t bytes = n * m;
if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    /* allocate “bytes” space */
}
Run Code Online (Sandbox Code Playgroud)

样本2.

if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    size_t bytes = n * m;
    /* allocate “bytes” space */
}
Run Code Online (Sandbox Code Playgroud)

样本3.

if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    size_t bytes = (size_t)n * (size_t)m;
    /* allocate “bytes” space */
}
Run Code Online (Sandbox Code Playgroud)

我认为他们都错了,但不是出于同样的原因.那么什么是正确的?

这些片段取自这里.


编辑强调主要问题是乘以有符号整数,这可能导致未定义的行为(未签名的行为).

我现在认为最后一个样本正常工作,只要整数,有符号整数,size_t和SIZE_MAX具有"通常"值,或者至少它们符合相关标准.

dav*_*pfx 0

示例 1 不正确,因为 的乘法m * n可能超出有符号整数类型的可表示值的范围,从而导致未定义的行为。参见 n1570 S6.5/5:

如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。

这具有实际重要性,因为编译器被允许做的事情之一就是假设未定义行为没有发生,并且它可以使用它来优化代码,以便该if语句消失并且不起作用。

请注意,这仅适用于有符号类型。无符号算术不会溢出。请参阅 n1570 S6.2.5/9。

涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会按比结果类型可以表示的最大值大 1 的数字进行减模。

示例 2 不正确,因为 的乘法m * n被计算为有符号整数表达式,这可能再次导致未定义的行为。但在这种情况下,产生实际效果的可能性较小。结果很可能会产生正确的按位结果,并且一旦转换为无符号,该值很可能是正确的。该示例是错误的,但更好,至少对于 32 位处理器来说是这样。

unsigned int编辑:如果和 的size_t大小相同,则上述段落为真,因为它们通常适用于 32 位处理器。size_t当 大于 时,示例 2 将无法正常工作unsigned int,因为它通常在 64 位处理器上。有符号乘法会溢出,并对某些值产生错误的行为。

样本 3 几乎肯定是正确的。将乘法m * n计算为无符号整数表达式时,不可能出现未定义行为。[正如另一个答案中所指出的,实际上只需要一次强制转换操作。]

这里的确切行为微妙地取决于int、unsigned int和size_t的定义SIZE_MAX。通常的假设是 与size_t相同unsigned int(或可能更大)并且是SIZE_MAX无符号整数文字,但这些实际上并不是要求。事实上,SIZE_MAX只需要至少65535。至少size_t要求未签名。

由于我无法找到任何可能导致示例失败的合理(甚至难以置信)的定义,因此我认为示例 3 可以正常工作。