The difference lies in their exact-ness and availability.
The doc here says:
unsigned integer type with width of exactly 8, 16, 32 and 64 bits respectively (provided only if the implementation directly supports the type):
uint8_t
uint16_t
uint32_t
uint64_t
And
fastest unsigned unsigned integer type with width of at least 8, 16, 32 and 64 bits respectively
uint_fast8_t
uint_fast16_t
uint_fast32_t
uint_fast64_t
So the difference is pretty much clear that uint32_t
is a type which has exactly 32
bits, and an implementation should provide it only if it has type with exactly 32 bits, and then it can typedef that type as uint32_t
. This means, uint32_t
may or may not be available.
On the other hand, uint_fast32_t
is a type which has at least 32 bits, which also means, if an implementation may typedef uint32_t
as uint_fast32_t
if it provides uint32_t
. If it doesn't provide uint32_t
, then uint_fast32_t
could be a typedef of any type which has at least 32
bits.