That is how a specific implementation defines them. The standard requires an int_fast8_t to be at least 8 bits. And the purpose of them is that the implementation should use the fastest of them, but there are no strict requirements about this or about how to measure it.
C11 standard 7.20.1.3 says
1 Each of the following types designates an integer type that is usually fastest(262) to operate with among all integer types that have at least the specified width.
2 The typedef name int_fastN_t designates the fastest signed integer type with a width of at least N . The typedef name uint_fastN_t designates the fastest unsigned integer type with a width of at least N .
3 The following types are required:
int_fast8_t uint_fast8_t
int_fast16_t uint_fast16_t
int_fast32_t uint_fast32_t
int_fast64_t uint_fast64_t
All other types of this form are optional.
The note linked says:
- The designated type is not guaranteed to be fastest for all purposes; if the implementation has no clear grounds for choosing one type over another, it will simply pick some integer type satisfying the signedness and width requirements.
Typically, the "fast" variables are defined to be the native size for the target processor, provided it is at least as big as the specified size. Note that it depends on the situation which is faster. When you have a single variable that you do operations on, it's typically best with the native size, but on large arrays, you instead typically want the smallest type that fits your needs, because the larger an array is in raw bytes, the worse it is for the cache.
For instance, Take the Open Watcom for DOS. There you have this:
typedef signed int int_fast8_t;
typedef signed int int_fast16_t;
typedef signed long int_fast32_t;
typedef signed long long int_fast64_t;
Notice that it's a difference between the 32 bit and 16 bit, which it is not in your example.
On my gcc implementation I have this:
typedef unsigned char uint_fast8_t;
typedef unsigned long int uint_fast16_t;
typedef unsigned long int uint_fast32_t;
typedef unsigned long int uint_fast64_t;
AFIK int8_t will behave exactly like int_fast8_t in almost all situations. But there are some situations where you might want to be careful. Look at this program:
#include <stdio.h>
#include <stdint.h>
int main(void) {
int_fast16_t array[] = { 12345,23456,34567 };
int16_t *ptr = array;
for(int i=0; i<3; i++) {
printf("%d %d\n", ptr[i], array[i]);
}
}
It gives me this warning:
$ gcc main.c
main.c: In function ‘main’:
main.c:6:20: warning: initialization of ‘int16_t *’ {aka ‘short int *’} from incompatible pointer type ‘int_fast16_t *’ {aka ‘long int *’} [-Wincompatible-pointer-types]
6 | int16_t *ptr = array;
| ^~~~~
And prints:
$ ./a.out
12345 12345
0 23456
0 34567
There are basically three categories of these types:
- Exact width
- Minimum width
- Fastest minimum width
Exact width has to be exact, but the two others have the same requirements but different purposes.
In an embedded system, I'd choose exact width for arrays and fastest (or simply just int or long) for regular variables. I cannot really see a good use for minimum width if exact width exists, except for portability. Here I want t point out that minimum and fastest are required by the standard, but exact width are not. Most systems do support exact width. So unless you want 100% portability or know for a fact that you need to compile the code on a machine that does not support it, I'd always prefer exact width over minimum width.