Search code examples
cgccprintfkeilinteger-promotion

C sprintf breaks with byte parameters (Keil compiler)


I have code running in 2 projects/platforms. It works in one, not the other. Code is like this:

uint8_t val = 1;
uint8_t buff[16];
sprintf(buff, "%u", val);

The expected output is "1" (gcc) but on one compiler (Keil) it returns "511", which in hex is 0x1FF. Looks like its not promoting the byte to int with this compiler. This is confirmed because it works ok if I do this:

sprintf(buff, "%u", (int)val);

My question is this: why does one compiler do what I consider the 'right thing', and one does not? Is it my incorrect expectations/assumptions, a compiler setting, or something else?


Solution

  • Your assumption may be correct, or incorrect. It depends on the compiler implementation. All modern (or should say smart) compiler will do that like you mentioned. But Keil, as of ver. 9.02, you need to specify correct variable length for printf.

    This is Keil C's way handling all kinds of printf functions. You need to specify exactly how long it is. All regular are for 16-bit (unsigned) integer including %d, %x, and %u. Use modifier 'b' for 8-bit and 'l' for 32-bit. If you gave wrong length, you would get wrong number. Even worse, the rest of the variables are all wrong. For example, to use 8-bit 'char', you use '%bd' (%bu, and %bx), and %ld, %lu, and %lx for 32-bit 'long'.

    char c = 0xab;
    printf("My char number is correctly displayed as '0x%02bx'\n", c);
    

    Also note, likewise, to get numeric data from sscanf, it's same. The following example is to get a 32-bit long variable using sscanf:

    long var;
    char *mynum = "12345678";
    sscanf(mynum, "%ld", &var);
    

    Variable var contains number 12345678 after sscanf. Hereunder shows the length for variables used in printf family for Keil.

    %bd, %bx, %bu - should be used for 8-bit variables

    %d, %x, %u - should be used for 16-bit variables, and

    %ld, %lx, %lu - should be used for 32-bit variables