You are likely wondering what are uint8_t, uint16_t, uint32_t and uint64_t.
That’s a good question. Because it could be really helpul!

It turns out that they are equal respectively to: unsigned char, unsigned short, unsigned int and unsigned long long.
But what are ranges of all these types?

Let’s test it in this C type tutorial.

Explanation

We’re going to use a variable called testValue equal to 0xFFFFFFFFFFFFFFFF.

Notice that 0xFFFFFFFFFFFFFFFF is the same as 18**,446,744,073,709,**551,615 and this is the maximum value possible for an unsigned long long, depending on your processor architecture (as gineera said in its comment).

It’s not so easy to understand all these things, but keep trying, it will be clearer after the end of this tutorial. At least, I hope it.

In the code part we will see that the number8 variable has a result of 255.
Why? Because 255 is the maximum value of an unsigned char or an uint8_t.
So if we put a value of 256, our result would be 0.
Indeed, after 255 we go back to 0.

For example if we added +1 for each number below, we’d have:

0 (First value of a char or 1 byte)
1
2
3
...
250
251
252
253
254
255 (Max value of a char or 1 byte)
0 (First value of a char or 1 byte)
1
2
3
... until 255 (Max value of a char or 1 byte)
0
1
2
3
... until 255 (Max value of a char or 1 byte)
0
1
2
3
and so on.

So we won’t be able to have a value of 256 in a char (or a byte).
If we wanted to have a such value, we would have to use another type, for example an unsigned short or an uint16_t equal to 2 bytes or 16 bits.

Wow, this is still confuse? Let’s continue!

Indeed, with an unsigned short, we will be able to use this type up a value of 65535 in decimal
or 0xFFFF in hex.

But in our example, we’re going to use a huge value: 18,446,744,073,709,551,615.

And what we’ll have will be the max value of each type!

Because this huge value is the maximum value of an unsigned long long.
So every type is set at the maximum value because they are a multiple of each maximum.

2^2 = 4
2^4 = 16
2^8 = 256
2^16 = 65536
2^32 = 4294967296
2^64 = 18446744073709551616

OK, but why the maximum value of a byte is 255 and not 256?
Because (2^8) - 1 = 256 - 1 = 255.
Indeed, our first value is 0 and not 1.
So the second is 1, the third is 2, and so on.
Thus, our last value is 255.

Code

// testValue
unsigned long long testValue     = 0xFFFFFFFFFFFFFFFF; // 18446744073709551615
// 1 byte -> [0-255] or [0x00-0xFF]
uint8_t         number8     = testValue; // 255
unsigned char    numberChar    = testValue; // 255
// 2 bytes -> [0-65535] or [0x0000-0xFFFF]
uint16_t         number16     = testValue; // 65535
unsigned short    numberShort    = testValue; // 65535
// 4 bytes -> [0-4294967295] or [0x00000000-0xFFFFFFFF]
uint32_t         number32     = testValue; // 4294967295
unsigned int     numberInt    = testValue; // 4294967295
 // 8 bytes -> [0-18446744073709551615] or [0x0000000000000000-0xFFFFFFFFFFFFFFFF]
uint64_t             number64         = testValue; // 18446744073709551615
unsigned long long     numberLongLong    = testValue; // 18446744073709551615

Conclusion

Now you are able to handle bits and bytes like a professional.
Well done, you’ve made it.