函数 sscanf 必须分配给变量否则奇怪的行为
Posted
技术标签:
【中文标题】函数 sscanf 必须分配给变量否则奇怪的行为【英文标题】:Function sscanf must be assigned to variable otherwise strange behavior 【发布时间】:2016-12-19 11:25:48 【问题描述】:考虑这段代码:
#define TRANSLATOR_requestElectricityMeterWrite() doaddr = 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 工具链
【问题讨论】:
WORD_getValueString()
和 WORD_getNameString()
定义在哪里?您似乎向我们展示了您的代码的不同版本。
在另一个文件中。但他们的声明包括在内。构建时既未报告警告也未报告错误。
我建议发Minimal, Complete, and Verifiable code
这是什么:if(dispatcher()) continue;
?
continue
外循环。您的编译器应该发出警告。为什么不呢?您是否至少启用了推荐的警告? “崩溃”是什么意思?这是哪个“默认处理程序”?异常寄存器显示什么?
【参考方案1】:
这两个函数都会调用未定义的行为,因此任何事情都可能发生。添加额外的局部变量会更改目标变量的位置,从而隐藏其不正确大小的影响。
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 个十六进制数字,因此不会发生溢出。
【讨论】:
返回类型uint8_t
和uint16_t
是否显式转换返回值?因为现在我返回unsigned int
,在我的平台上是uint32_t
。
这些函数返回的unsigned int
值分别隐式转换为uint8_t
和uint16_t
返回类型。转换是完全定义的,值被截断,并且考虑到它们是由sscanf
计算的,它们在返回类型的范围内。
太好了,所以你提供的例子做得很好。我不得不承认我对我正在调试的电路板的行为感到非常沮丧。这对我和其他 C 语言危险的新手来说是一个很好的例子,尤其是在涉及内存时。 10x :) @chqrlie
@Hairi:转换定义明确,但仍可能导致问题。启用转换警告(和所有其他相关警告)。如果您收到这样的警告,请添加显式强制转换以使编译器静音仅当您 200% 确定转换将是正确的在所有情况下(与添加时一样演员)。【参考方案2】:
%x
格式需要unsigned
参数(假设它在您的平台上是uint32_t
)。如果您通过uint16_t
或uint8_t
它可能会损坏内存。在您的情况下,它会损坏堆栈并覆盖返回地址。尝试将%4hx
用于uint16_t
,将%2hhx
用于uint8_t
。
【讨论】:
%4hx
假定目的地是unsigned short
。虽然可能是这样,但它在技术上仍然存在风险,因为我们不知道 uint16_t
是否与 unsigned short
是同一类型。以上是关于函数 sscanf 必须分配给变量否则奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章
定义指针变量时,必须将指针变量初始化为NULL(为空),否则,指针变量会由于初始化位置的不确定