Você pode usar hhpara dizer printfque o argumento é um char sem sinal. Use 0para obter preenchimento zero e 2definir a largura como 2. xou Xpara caracteres hexadecimais em maiúsculas / minúsculas.
uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"
Edit : Se os leitores estão preocupados com a afirmação de 2501 de que este não é, de alguma forma, os especificadores de formato 'corretos', sugiro que leiam o printflink novamente. Especificamente:
Mesmo que% c espere um argumento int, é seguro passar um caractere por causa da promoção de inteiro que ocorre quando uma função variável é chamada.
As especificações de conversão corretas para os tipos de caracteres de largura fixa (int8_t, etc) são definidas no cabeçalho <cinttypes>(C ++) ou <inttypes.h>(C) (embora PRIdMAX, PRIuMAX, etc seja sinônimo de% jd,% ju, etc) .
Quanto ao seu ponto sobre assinado vs não assinado, neste caso não importa, pois os valores devem ser sempre positivos e facilmente caber em um int assinado. De qualquer maneira, não há especificador de formato hexideximal assinado.
Edição 2 : ( edição "quando-admitir-que-você-está-errado"):
Se você ler o padrão C11 real na página 311 (329 do PDF), encontrará:
hh: especifica que a seguinte d, i, o, u, x, ou Xespecificador de conversão aplica-se a um signed charou unsigned charargumento (o argumento terá sido promovidos de acordo com as promoções de inteiros, mas o seu valor deve ser convertido em signed charou unsigned charantes da impressão); ou que um nespecificador de conversão seguinte se aplica a um ponteiro para um signed charargumento.
"\xc0\xc0abc123"