Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

uint8_t not rollover to 0 after reaching 255 not working properly

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.

like image 691
TejasKhajanchee Avatar asked May 09 '19 08:05

TejasKhajanchee


2 Answers

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).

like image 143
Serge Ballesta Avatar answered Sep 17 '22 07:09

Serge Ballesta


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.

like image 41
Lundin Avatar answered Sep 20 '22 07:09

Lundin