'wrapping' 乘法溢出的结果是什么?

What is the result of 'wrapping' a multiplication overflow?

bcache 源 here 包含以下行:

schedule_delayed_work(&dc->writeback_rate_update,
    dc->writeback_rate_update_seconds * HZ);

writeback_rate_update_seconds 被定义为 unsigned intappears to be 32 bit 在 x86_64 上,我不确定 HZ 有什么类型,但我相信它的价值是 1000 并假设它是 32 位或更少。

如果我将 writeback_rate_update_seconds 设置为 2147483647,实际传递给 schedule_delayed_work 的值是多少? schedule_delayed_work 的第二个参数似乎是 long,但这并不意味着操作数在乘法溢出之前被提升为 long,对吗?

鉴于:

#include <stdio.h>
#include <stdlib.h>

int schedule_delayed_work( unsigned long param )
{
    printf("value: %lu\n", param);
    return 0;
}

int main(int argc, char **argv)
{
    unsigned int writeback_rate_update_seconds;
    unsigned int HZ;
    writeback_rate_update_seconds = 2147483647;
    HZ = 1000;
    schedule_delayed_work( writeback_rate_update_seconds * HZ );
    return 0;
}

您将 4294966296 传递给函数。

如果将函数调用更改为强制转换:

schedule_delayed_work( (unsigned long) writeback_rate_update_seconds * HZ );

...您将 2147483647000 传递给函数。

我没有查看 C 标准以了解标准行为是什么,但这是通过以下方式测试的:

Apple LLVM version 8.1.0 (clang-802.0.38)
Target: x86_64-apple-darwin16.7.0

如果两个操作数都在 unsigned int 中(如果 HZ 是常量 1000,它是 int 类型并且在 unsigned int 中)它们'重新晋升为unsigned int。对于 unsigned 整数,溢出是明确定义的;结果值是计算模值(UINT_MAX 加一)。即最大结果为UINT_MAXUINT_MAX + 1 将得到 0,UINT_MAX + 2 将得到 1,依此类推。

接收器的类型(这里是接收结果的参数的类型)根本不重要。为避免环绕,将其中一个参数转换为 更宽的整数类型 (例如 unsigned long 在 64 位 Linux 中是 64 位;或者更好,使用固定宽度的类型,例如 uint64_t).