考虑以下代码:
#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工具链
答案 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_t
或uint8_t
,则可能会损坏内存。在您的情况下,它会损坏堆栈并覆盖返回地址。尝试%4hx
使用uint16_t
,%2hhx
使用uint8_t
。