A diferença está na exatidão e disponibilidade.
O doc aqui diz:
tipo inteiro sem sinal com largura de exatamente 8, 16, 32 e 64 bits, respectivamente ( fornecido apenas se a implementação suportar diretamente o tipo ):
uint8_t
uint16_t
uint32_t
uint64_t
E
tipo inteiro não assinado não assinado mais rápido com largura de pelo menos 8, 16, 32 e 64 bits, respectivamente
uint_fast8_t
uint_fast16_t
uint_fast32_t
uint_fast64_t
Portanto, a diferença é bastante clara: uint32_t
é um tipo que tem exatamente 32
bits, e uma implementação deve fornecê-lo apenas se tiver exatamente 32 bits, e então pode definir esse tipo como uint32_t
. Isso significa que uint32_t
pode ou não estar disponível .
Por outro lado, uint_fast32_t
é um tipo que possui pelo menos 32 bits, o que também significa, se uma implementação pode ser digitada uint32_t
como uint_fast32_t
se ela fornecesse uint32_t
. Se não fornecer uint32_t
, uint_fast32_t
pode ser um typedef de qualquer tipo que tenha pelo menos 32
bits.