依靠溢出是危险的吗?

时间:2013-12-06 16:32:56

标签: c++ c timer delay counter

以下是我在固件中找到的延迟功能。它看起来有点危险,或者至少让读者感到困惑。

全局变量

static int32u masterTimerCounter; // 32-bit unsigned timer counter

系统节拍中断处理程序

/* Called Every Millisecond */
void sysTickIrqHandler(void)
{
    masterTimerCounter++;
}

设置计时器到期功能

void setTimerExpiration(int32u *timerExpiration, int32u delay)
{
    *timerExpiration = masterTimerCounter + delay;
}

检查定时器是否过期功能

boolean timerExpired(int32u timerExpiration, int32u delay)
{
    if((masterTimerCounter - timerExpiration) < delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

设置定时器释放并阻止定时器过期

int32u timerExpiration;
setTimerExpiration(&timerExpiration, 15); // Set expiration timer to 15 milliseconds
while(!timerExpired(timerExpiration, 15) // Block until timer has expired
    continue; 

问题

正如您在timerExpired()中看到的那样,masterTimerCountertimerExpiration减去。如果计时器尚未到期,则计算将导致非常大的数量(因为两个操作数都是无符号数)。当计时器到期时,计算将产生小于延迟量的值。

虽然这似乎工作得很好,但似乎它可能很危险,或者至少让读者感到困惑(我必须多次阅读才能理解原始程序员的意图)。

如果我必须写一些与此类似的东西,我会按如下方式定义timerExpired函数:

boolean timerExpired(int32u timerExpiration)
{
    if(timerExpiration > masterTimerCounter)
        return FALSE; // Timer still active
    else
        return TRUE; // Timer has expired
}

我应该重新定义'timerExpired()`吗?

注意:功能和变量名称已被更改以保护无辜者。

3 个答案:

答案 0 :(得分:5)

请注意,原始逻辑类似于:是过去的绝对到期时间,但不到完整的延迟时间。也许我们可以松散地表达它,因为最近这个计时器开火了。

您修改过的逻辑只是是过去的绝对到期时间,这是不同的。


只需在不等式的每一边添加timerExpiration,即可轻松避免下溢的风险:

boolean timerExpired(int32u timerExpiration, int32u delay)
{
// WAS: (masterTimerCounter - timerExpiration) < delay
    if(masterTimerCounter < timerExpiration + delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

但是这会改变行为,因为如果masterTimerCounter < timerExpiration你说原作将永远是假的。您可以通过明确地检查它来获得原始行为,而不会产生混乱的下溢:

boolean timerExpired(int32u timerExpiration, int32u delay)
{
    if(masterTimerCounter > timerExpiration &&       // did it expire ...
       masterTimerCounter < timerExpiration + delay) // ... recently?
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

答案 1 :(得分:2)

固件代码毫无意义。

int32u expire;
setTimerExpiration(&expire, 0);
timerExpired(expire, 0); // is always false, unless the timer overflows 

答案 2 :(得分:1)

问题在于,如果masterTimerCounter + delay导致32位int的翻转,则会立即通过timerExpired测试。

我认为在存在可能的翻转时执行整数计时器的最直接的方法是这样的:

void startTimer(int32u *timerValue)
{
    *timerValue = masterTimerCounter;
}

检查定时器是否过期功能

boolean timerExpired(int32u timerVal, int32u delay)
{
    if ((masterTimerCounter - timerVal) >= delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

用法:

int32u timer;
startTimer(&timer); // Start timing
while(!timerExpired(timer, 15) // Block for 15 ticks
    continue;

即使timerExpired中的减法下溢,也会返回正确的结果。