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

时间:2016-08-12 12:34:27

标签: c gdb embedded gnu gnu-arm

考虑以下代码:

#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;
}

有效。如果虚拟i似乎解决了这个问题。但为什么它不起作用呢?

GNU ARM v4.8.3工具链

2 个答案:

答案 0 :(得分:3)

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

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并将解析后的值分别存储到目标,或者只返回值:

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个十六进制数字,因此不会发生溢出。

答案 1 :(得分:1)

%x格式需要unsigned参数(假设您的平台上为uint32_t)。如果您通过uint16_tuint8_t,则可能会损坏内存。在您的情况下,它会损坏堆栈并覆盖返回地址。尝试%4hx使用uint16_t%2hhx使用uint8_t