Variable arguments are subject to the default argument promotion, eg char will be promoted to int, float to double (that's the reason why you only have a single format specifier to print both single and double precision floating point values).
So passing an int instead of a char is perfectly valid and even desirable, as character literals are of type int anyway. According to the C99 spec, section 7.19.6.1 §8, on seeing the conversion specifier %c, printf() expects an argument of type int and will then go on and cast this value to unsigned char.
This means the following is guaranteed to output a, as conversion of signed to unsigned types is well-defined:
int a = 'a' + UCHAR_MAX + 1;
printf("%c", a);