• 两个无符号数相减的问题


    因为一个裸机软定时器的溢出问题,

    static uint32_t _timer_ticks = 0;
    
    void timer_ticks()
    {
        _timer_ticks++;
    }
    
    void soft_timer_loop()
    {
        struct soft_timer* target;
        for(target = head_handler; target; target = target->next)
        {
            if(_timer_ticks >= target->timeout) //这里有机会溢出 是个bug
            {
                if(target->repeat == 0)
                {
                    soft_timer_stop(target);
                } 
                else 
                {
                    target->timeout = _timer_ticks + target->repeat; 
                }
                target->timeout_cb();
            }
        }
    }

    为了解决这个bug,有人可以把判断条件换成

    if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) 
    {
        
    }

    这个涉及到2个无符号数相减的问题。
    微机原理中计算通过ALU计算,加减的时候,ALU不认识什么符号。他有两个输入端A,B。 A端直接输入,B端的看情况,如果是+则直接输入,如果是-则经过一个反向器并加1输入。 他只管计算,并把结果反回给程序,至于这个结果是什么类型,就由程序来处理了

    简单一句,减法运算,就是减数取反后加1,然后做加法。

    这里假设_timer_ticks和 target->timeout都是8位的。target->timeout +_timer_ticks 溢出了。1ms计数的情况下,按实际 target->timeout是要比4294967295(49.7天)要小的多的一个数。
    这里按8位数写
    假设_timer_ticks = 250 ,target->repeat = 10 则 target->timeout = 4 
    则target->timeout -_timer_ticks  
    _timer_ticks 原码 1111 1010
                        补码 0000 0110
    target->timeout + 0000 0110 = 16强制转换为int 也是大于0的。
    依次 _timer_ticks = 251   (int)((uint32_t)(target->timeout -_timer_ticks)) = 15
    。。。
    _timer_ticks = 255 (int)((uint32_t)(target->timeout -_timer_ticks)) = 11
    。。。
    _timer_ticks = 10  (int)((uint32_t)(target->timeout -_timer_ticks)) = 1

    _timer_ticks = 11  (int)((uint32_t)(target->timeout -_timer_ticks)) = 0

    _timer_ticks = 12  (int)((uint32_t)(target->timeout -_timer_ticks)) < 0

    所以解决了溢出的问题。

    那么这么些有bug吗?首先没有溢出的情况下,很好理解,这时候其实不用考虑什么补码,不用再绕一层,按常识走。
    就是溢出的情况下,有,假设在_timer_ticks = 128的时候  target->repeat = 128 ,这是target->timeout = 1
    _timer_ticks = 129  (int)((uint32_t)(target->timeout -_timer_ticks)) = -128 这tm就成立了啊。

    或者说_timer_ticks = 128的时候  target->repeat = 129 这是target->timeout = 2
    _timer_ticks = 129  (int)((uint32_t)(target->timeout -_timer_ticks)) = -129 这tm就成立了啊。
    _timer_ticks = 130  (int)((uint32_t)(target->timeout -_timer_ticks)) = -128 这tm就成立了啊。

    这里再极限一些 target->repeat = 250,当_timer_ticks = 6的时候,target->timeout = 1
    _timer_ticks = 2  (int)((uint32_t)(target->timeout -_timer_ticks)) = -1 这tm就成立了啊。

    这里只要target->repeat <= 127(UINT8_MAX/2) 就不会出现这种bug,而uint32的一半要20多天,所以这个bug,注意一下就行,平时不会出现。

    还有一种方法,就是_timer_ticks target->timeout定义为64位的 那样溢出需要1亿年,根本不会溢出了。

  • 相关阅读:
    Spark学习(一)Spark初识
    service mysqld restart mysqld: 未被识别的服务
    Spark学习(二)Spark 版本 WordCount
    java.sql.SQLException: Incorrect string value: '\xE4\xB8\xAD\xE9\x83\xA8' for column 'area' at row 1
    centos 6.8 yum源不可用安装报YumRepo Error: All mirror URLs are not using ftp, http[s] or file
    互联网运维装腔指南
    PHP生成一段时间之间的月份列表
    sql根据分组后排序取第一条数据
    sql 多行拼接 成一行
    js 常用汇总
  • 原文地址:https://www.cnblogs.com/Rainingday/p/14167408.html
Copyright © 2020-2023  润新知