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

时间:2017-06-01 09:42:34

标签: c integer-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,但这并不意味着在乘法溢出之前操作数被提升为很长时间,是吗?

2 个答案:

答案 0 :(得分:3)

假设:

#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传递给该函数。

如果将函数调用更改为cast:

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

答案 1 :(得分:2)

如果两个操作数都适合unsigned int(如果HZ为常数1000,则类型为int并且适合unsigned int)它们会被提升到unsigned int。使用unsigned整数,溢出是明确定义的;结果值是计算的模数(UINT_MAX加1)。也就是说,最大结果是UINT_MAX; UINT_MAX + 1将导致0,UINT_MAX + 2将导致1,依此类推。

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