Take a look at the following code — on an x86_64 system, what do you think it will output?
char a = 0xa1;
printf("%02x", a);
Do you think it will print "a1"?
Wrong!!
The actual output is: ffffffa1
Yes, you read that right — it prints the hexadecimal representation of a negative int.
🔍 Why does this happen?
In C, when you use the %x or %02x format specifier with printf, the argument undergoes integer promotion — all integer types smaller than int (such as char or short) are automatically promoted to int (or, rarely, unsigned int) before being passed to printf. This is part of C’s “default argument promotions.”
📜 According to the C standard (C17 §6.3.1.1):
If an
intcan represent all values of the original type, the value is promoted toint; otherwise, it is promoted tounsigned int.
On mainstream platforms (like x86_64 or x86_32), int is 32-bit (4 bytes), and char is 8-bit — so char is always promoted to int.
🧪 Back to our example:
char a = 0xa1; // On platforms where char is signed, 0xa1 = 161 > 127 → interpreted as negative (-95)
When promoted to int, sign extension occurs — the high bits are filled with 1s, resulting in:
0xffffffa1 (32-bit)
Then, %x treats this int as an unsigned int for printing, so you get ffffffa1.
💡 Important: The %x specifier always interprets its argument as unsigned int, regardless of whether the original value was negative!
📏 About the 02 in %02x:
Many people mistakenly believe %02x will “truncate” the output to 2 digits — but that’s not true.
02 means: minimum width of 2 characters, padded with leading zeros if shorter; if longer, print all digits.
Example:
int a = 0xabc;
printf("%02x", a); // Output: "abc" — NOT "bc" or "0abc"
✅ The correct approach: explicitly cast to unsigned char
If you want to print only the 2-digit hex value of a single byte, cast it to unsigned char:
char a = 0xa1;
printf("%02x", (unsigned char)a); // ✅ Output: "a1"
This way, 0xa1 is treated as the unsigned value 161, promoted to int(161), and printed by %x as "a1" — perfect!
Pretty neat, huh? Another “gentle trap” in C you’ve just uncovered 😄
Next time you print byte data, remember to add (unsigned char) — your debugging life will become much smoother!
👏 Feel free to like, bookmark, and share!
