2
votes

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?

2
Try sprintf(buf,"%hhu",val);, does it work? - Alex Lop.
IMO, the Keil compiler has a bug. You should check to see whether it is known. If not, or if you can't tell, you should report it. The argument val is in the variadic part of the argument list and must be promoted to int before being passed. I don't think 0x1FF is a valid way to promote the byte. - Jonathan Leffler
@AlexLop.: My gut feel is that (a) it probably won't work, and (b) if it does work, it is at most a workaround, not the required behaviour. The uint8_t should be promoted cleanly to an int in the call. Even if int is a 16-bit type, I don't see a way for 0x1FF to be a valid promotion of the value 1. - Jonathan Leffler
@JonathanLeffler yes, it is a possible work arround and probably it is a compiler bug BUT it can be also caused by some undefined behavior in the other parts of the code. If we see the whole code we can better understand such behaviour. - Alex Lop.
@AlexLop Not sure what any other context would add here. I too think it's a compiler bug. Didn't get to try that option; might get to it next week when the project is back up. Thanks. - radsdau

2 Answers

2
votes

For maximum portability, you can use these macros from inttypes.h: (there are others)

PRId8, PRIx8, PRIu8 PRId16, PRIx16, PRIu16 PRId32, PRIx32, PRIu32

Normally (as I expected):

#define PRIu8 "u"

But for the Keil compiler in this case:

#define PRIu8 "bu"

e.g.,

printf("0x%"PRIx8" %"PRIu16"\n", byteValue, wordValue);

That's pretty cumbersome though. I suggest more friendly compilers.

It's amazing what you don't know about this stuff even after doing it for decades.

1
votes

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