Hairi Hairi - 3 months ago 18
C Question

Function sscanf must be assigned to variable otherwise strange behavior

Consider this code:

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


When the code above is run, the statement inside
if
causes program to crash(goes to some default handler).

But when I change the two(
word_getValue
and word_
getAddres
) functions to this:

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


It works. The addition if the dummy
i
seems to solve that problem. But why doesn't it work the other way?

GNU ARM v4.8.3 toolchain

Answer

Both functions invoke undefined behavior, hence anything can happen. Adding an extra local variable changes the location of the destination variable, hiding the effect of its incorrect size.

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

sscanf will store sizeof(unsigned int) bytes (probably 4) into variable value, which has only 2 bytes.

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

Will store sizeof(unsigned int) bytes into variable address, which has only 1 byte.

The easiest way to fix this problem is to parse into an unsigned int and store the parsed value to the destination separately, or simply return the value:

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

You might also want to verify if the parsed value is within range for the destination type, but since you limit the parse to respectively 4 and 2 hex digits, overflow cannot happen.

Comments