2

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?

M.M
  • 138,810
  • 21
  • 208
  • 365
radsdau
  • 495
  • 5
  • 16
  • Try `sprintf(buf,"%hhu",val);`, does it work? – Alex Lop. Sep 18 '15 at 04:40
  • 1
    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 Sep 18 '15 at 04:41
  • 2
    @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 Sep 18 '15 at 04:42
  • @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. Sep 18 '15 at 05:02
  • @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 Sep 18 '15 at 07:04
  • Long ago, I used a version of Keil C51 compiler that did not correctly perform the default argument promotions. You had to write `sprintf(buff, "%u", (int)val);`. This is of course a compiler bug, and a pretty egregious one. – M.M Nov 18 '15 at 04:15

2 Answers2

2

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.

radsdau
  • 495
  • 5
  • 16
1

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

Community
  • 1
  • 1
Tim
  • 80
  • 1
  • 1
  • 9
  • 1
    Thanks Tim. I don't really like the Keil plan at all, but it's good to be aware of it. I suppose it makes sense for baby embedded micros with limited resources. – radsdau Nov 10 '15 at 23:29
  • Can you substantiate your thanks by marking the answer? – Tim Nov 12 '15 at 07:46
  • 1
    Done. Can you edit the sprintf that are supposed to be sscanf? I made the edit but it was rejected, don't know why. sprintf(mynum, "%ld", var); should be sscanf(mynum, "%ld", &var); etc. – radsdau Nov 17 '15 at 00:41
  • 1
    Thanks for the info. It failed the first time. Changed again and made it. – Tim Nov 18 '15 at 20:49