La diferencia radica en su exactitud y disponibilidad.
El doc aquí dice:
tipo entero sin signo con un ancho de exactamente 8, 16, 32 y 64 bits respectivamente ( proporcionado solo si la implementación admite directamente el tipo ):
uint8_t
uint16_t
uint32_t
uint64_t
Y
el tipo entero sin signo sin signo más rápido con un ancho de al menos 8, 16, 32 y 64 bits respectivamente
uint_fast8_t
uint_fast16_t
uint_fast32_t
uint_fast64_t
Entonces, la diferencia es bastante clara: uint32_tes un tipo que tiene exactamente 32 bits, y una implementación debe proporcionarlo solo si tiene un tipo con exactamente 32 bits, y luego puede typedef ese tipo como uint32_t. Esto significa uint32_tque puede estar disponible o no .
Por otro lado, uint_fast32_tes un tipo que tiene al menos 32 bits, lo que también significa que si una implementación puede typedef uint32_tcomo uint_fast32_t si lo proporciona uint32_t. Si no lo proporciona uint32_t, entonces uint_fast32_tpodría ser un typedef de cualquier tipo que tenga al menos 32bits.