I am new to the C-Headers - stdint.h
and inttypes.h
. I was trying out some code to get an inkling of how uint8_t
works. But, it seems to have encountered a problem.
I have declared 4 uint8_t
integers with the boundary values 0, 255, 256, -1 respectively and performed some simple arithmetic operations on it. I did this since I wanted to know what errors/warnings does the c-compiler (I am using gcc 5.4.0
on linux) generate. And if not, I was interested in knowing what the output looked like. The code in given below.
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
int main() {
int a = 10;
printf("%d\n", a++);
printf("%d\n\n", a);
// 8 bit unsigned integer -> range[0, 255]
uint8_t ua81=0, ua82=255, ua83=256, ua84=-1;
printf("--------STDINT.H----uint8_t--DEMO--------\nua81 = %" PRIu8 "\n", ua81);
printf("ua82 = %" PRIu8 "\nua83 = %" PRIu8 "\nua84 = %" PRIu8 "\n\n", ua82, ua83, ua84);
printf("ua81+1 = %" PRIu8 "\nua82-3 = %" PRIu8 "\nua83-4+7 = %" PRIu8 "\nua84-1+20 = %" PRIu8 "\n----------\n\n", ua81+1, ua82-3, ua83-4+7, ua84-1+20);
return 0;
}
The output of this code is as follows:
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ vi stdint_h.c
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ gcc -Wall stdint_h.c -o a
stdint_h.c: In function ‘main’:
stdint_h.c:11:33: warning: large integer implicitly truncated to unsigned type [-Woverflow]
uint8_t ua81=0, ua82=255, ua83=256, ua84=-1;
^
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ ./a
10
11
--------STDINT.H----uint8_t--DEMO--------
ua81 = 0
ua82 = 255
ua83 = 0
ua84 = 255
ua81+1 = 1
ua82-3 = 252
ua83-4+7 = 3
ua84-1+20 = 274
----------
As mentioned earlier, I am using a Linux machine for this with the gcc 5.4.0
compiler.
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I am not able to understand why, for the variable ua84
, the value is not rolling over to 0
after reaching 255
, while the same is working for ua83
. I think the value, considering the rollover after 255, of ua84
should have been 18
.
I feel this has got something to do with the Implicit Truncation mentioned in the warning
that was generated pertaining to the variable ua83
while compiling. I don't know exactly what is wrong with that. I would also like to know how to go about that I still want to continue using uint8_t
.
Because of the implicit promotions, ua84-1+20
will be an int
and it will have the value 274. You should explicitely convert the result to uint8_t
to have it to become 18.
printf
does not come with enough magic to convert its arguments depending on the format(*). It only expects that it receives what it needs and just extract the passed parameters. So here you should write:
printf("ua81+1 = %" PRIu8 "\nua82-3 = %" PRIu8 "\nua83-4+7 = %" PRIu8 "\nua84-1+20 = %"
PRIu8 "\n----------\n\n", (uint8_t)(ua81+1), (uint8_t)(ua82-3), (uint8_t)(ua83-4+7),
(uint8_t)(ua84-1+20));
(*) More exactly, it is a problem with the format specifier. In paragraph 7.21.6.1 The fprintf function §7 draft n1570 for C11 says:
hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing); ...
So, if you have explicitely used %hhu
, you would have got 18 (even if a 274 had been passed). But unfortunately the macros from inttype.h are only required to correctly display a value of the expected range, so many implementations translate PRIu8
as u
(thank to Eric Postpischil for noticing that).
printf
family of functions is very dangerous and unstable, with non-existent type-safety. So if you provide an argument which is not of the type you specified with the format specifier, you invoke undefined behavior. This is what the programmers of the library will tell you.
The expression ua84-1+20
results in implicit type promotion to int
, see Implicit type promotion rules. So you lie to printf with PRIu8
, telling it to expect an uint8_t
while you pass an int
.
It can be fixed by converting back to the expected type: (uint8_t)(ua84-1+20)
, which will print 18 as expected.
Notably, all variadic functions like printf
also have a similar kind of implicit type promotion known as "the default argument promotions". These will actually convert the parameter to an int
no matter what you do. But the library is expecting that promotion to happen, so when you type PRIu8
followed by (uint8_t)whatever
, they expect to deal with (int)(uint8_t)whatever
internally.
However, it is mighty fishy that the library prints 274
, UB or not. This suggests that the printf
implementation is actually not converting the parameter back to uint8_t
internally. How the library can justify that is a good question, because as we can see it leads to bugs and less rugged code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With