In the below code snippet, why do we get an output of 600? Two questions will help me understand the behavior.
- I specified the type for variable b to be uint8_t, why doesn’t the math operation restrict to the same type?
- I thought PRIu8 is the right type to print uint8_t, why isn’t this working?
I was hoping to get the answer 88 which results from looping beyond the range of uint8_t
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
void main()
{
uint8_t b = 200;
printf("%" PRIu8 "n",b+b+b);
printf("%" PRIu8 "n",3*b);
}
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
3
Answers
If we take a look at inttypes.h, we find the following:
So this format specifier doesn’t contain any length modifier. This makes sense in the context of its corresponding argument, as it’s impossible to pass a
uint8_t
to a variadic function. Because this type has a smaller rank thanint
, a value of this type will get promoted toint
. This promotion also happens in the expressionsb+b+b
and3*b
.If you explicitly use a length modifier for a
char
, you’ll get the expected result.In my opinion it is a library bug.
In MS Visual Studio the macro
PRIu8
is expanded tohhu
as it should be and you get the expected result.It is interesting to note that if to use clang then if you will write for example
when again you will get the expected result.
In C, whenever you call a function with elipsis (
...
) arguments, such as printf, all of those arguments will undergo default argument promotions. This means that any smaller integer type will be converted toint
and passed that way. So in printf,h
type prefixes are irrelevant — they’ll be ignored by printf and have no effect (as the argument must be anint
anyways).In addition, the same conversion of smaller integer types will happen for any arithmetic operator (like
+
or*
) and the resulting operation will be carried out withint
precision, giving anint
result.To get the expected result, explicitly mask it to the appropriate size: