C++-兩個無符號數相減的問題


因為一個裸機軟定時器的溢出問題,

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輸入。 他只管計算,並把結果反回給程序,至於這個結果是什么類型,就由程序來處理了

簡單一句,無符號數的加減法等同於補碼進行加減法,只是最后對於結果的解釋不一樣,無符號數A-B,可以和補碼表示的有符號數一樣,轉化成,A+(B的補碼),即A+(~B+1 )

這里假設_timer_ticks和 target->timeout都是8位的。target->repeat +_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 = 10強制轉換為int 也是大於0的。
_timer_ticks = 251 ((uint8_t)(target->timeout -_timer_ticks)) = 0x09轉為有符號 (char)((uint8_t)(target->timeout -_timer_ticks)) = 9

。。。
_timer_ticks = 255 
((uint8_t)(target->timeout -_timer_ticks)) =0x05轉為有符號(char)((uint8_t)(target->timeout -_timer_ticks)) = 5
。。。
_timer_ticks = 3 
 ((uint8_t)(target->timeout -_timer_ticks)) = 0x01轉為有符號(char)((uint8_t)(target->timeout -_timer_ticks)) = 1

_timer_ticks = 4 
 ((uint8_t)(target->timeout -_timer_ticks)) = 0x00轉為有符號(char)((uint8_t)(target->timeout -_timer_ticks)) = 0

_timer_ticks = 5  ((uint8_t)(target->timeout -_timer_ticks)) = 0xff轉為有符號(char)((uint8_t)(target->timeout -_timer_ticks)) < -1

所以解決了溢出的的時候,因為target->timeout 必定比_timer_ticks小,而立即執行的BUG。


那么這么些有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億年,根本不會溢出了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM