函数 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_tuint16_t 是否显式转换返回值?因为现在我返回unsigned int,在我的平台上是uint32_t 这些函数返回的unsigned int 值分别隐式转换为uint8_tuint16_t 返回类型。转换是完全定义的,值被截断,并且考虑到它们是由sscanf 计算的,它们在返回类型的范围内。 太好了,所以你提供的例子做得很好。我不得不承认我对我正在调试的电路板的行为感到非常沮丧。这对我和其他 C 语言危险的新手来说是一个很好的例子,尤其是在涉及内存时。 10x :) @chqrlie @Hairi:转换定义明确,但仍可能导致问题。启用转换警告(和所有其他相关警告)。如果您收到这样的警告,请添加显式强制转换以使编译器静音仅当您 200% 确定转换将是正确的在所有情况下(与添加时一样演员)。【参考方案2】:

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

【讨论】:

%4hx 假定目的地是unsigned short。虽然可能是这样,但它在技术上仍然存在风险,因为我们不知道 uint16_t 是否与 unsigned short 是同一类型。

以上是关于函数 sscanf 必须分配给变量否则奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章

递归函数的内存分配

静态变量

定义指针变量时,必须将指针变量初始化为NULL(为空),否则,指针变量会由于初始化位置的不确定

C程序定义一指针变量,直接令它赋值为NULL,不对它用malloc申请内存可以么?赋值和申请内存到底有啥区别

第三期 行为预测——速度惩罚的代价函数

函数奇怪行为中的JQuery变量