必须将函数 sscanf 分配给变量,否则会出现奇怪的行为

Function sscanf must be assigned to variable otherwise strange behavior

考虑这段代码:

#define TRANSLATOR_requestElectricityMeterWrite()  do{addr = word_getAddress(); value = word_getValue(); }while(0)

uint16_t value;
uint8_t addr;

bool dispatcher(void)
{
    TRANSLATOR_requestElectricityMeterWrite(); 
    return true;
} // AFTER this point (during debug) program goes to default handler

int main(void)
{
   if(dispatcher())
      continue;
      . . . .
      . . . . 
}

uint16_t word_getValue(void)
{
    uint16_t value;
    sscanf("ABCD", "%4x", (unsigned int *)&value);
    return value;
}

uint8_t word_getAddress(void)
{
    uint8_t address;
    sscanf("00", "%2x", (unsigned int *)&address);
        ;
    return address;
}

当上面的代码是运行时,if中的语句导致程序崩溃(转到某些默认处理程序)。

但是当我将两个(word_getValue 和 word_getAddres)函数更改为:

uint16_t word_getValue(void)
{
    uint16_t value;
    int i = 0;i++;
    i = sscanf(WORD_getValueString(), "%4x", (unsigned int *)(&value));
    return value;
}

uint8_t word_getAddress(void)
{
    uint8_t address;
    int i = 0;i++;
    i = sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
    return address;
}

有效。添加 if the dummy i 似乎可以解决该问题。但为什么它不能以其他方式工作?

GNU ARM v4.8.3 工具链

%x 格式需要 unsigned 参数(假设它在您的平台上是 uint32_t)。如果你传递 uint16_tuint8_t 它会破坏内存。在您的情况下,它会损坏堆栈并覆盖 return 地址。尝试对 uint16_t 使用 %4hx,对 uint8_t 使用 %2hhx

这两个函数都会调用未定义的行为,因此任何事情都有可能发生。添加一个额外的局部变量会改变目标变量的位置,隐藏其不正确大小的影响。

sscanf("ABCD", "%4x", (unsigned int *)&value);

sscanf 会将 sizeof(unsigned int) 字节(可能是 4)存储到变量 value 中,它只有 2 个字节。

sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));

sizeof(unsigned int) 字节存储到变量 address 中,该变量只有 1 个字节。

解决此问题的最简单方法是解析为 unsigned int 并将解析后的值单独存储到目标,或者简单地 return 值:

uint16_t word_getValue(void) {
    unsigned int value;
    if (sscanf(WORD_getValueString(), "%4x", &value) == 1)
        return value;
    // could not parse a value, return some default value or error code
    return 0;
}

uint8_t word_getAddress(void) {
    unsigned int address;
    if (sscanf(WORD_getNameString(), "%2x", &address) == 1)
        return address;
    // could not parse a value, return some default value or error code
    return 0;
}

您可能还想验证解析的值是否在目标类型的范围内,但由于您分别将解析限制为 4 位和 2 位十六进制数字,因此不会发生溢出。