printf() formatting for hexadecimal
Why, when printing a number in hexadecimal as an 8 digit number with leading zeros, does %#08X
not display the same result as 0x%08X
?
When I try to use the former, the 08
formatting flag is removed, and it doesn't work with just 8
.
The #
part gives you a 0x
in the output string. The 0
and the x
count against your "8" characters listed in the 08
part. You need to ask for 10 characters if you want it to be the same.
int i = 7;
printf("%#010x\n", i); // gives 0x00000007
printf("0x%08x\n", i); // gives 0x00000007
printf("%#08x\n", i); // gives 0x000007
Also changing the case of x
, affects the casing of the outputted characters.
printf("%04x", 4779); // gives 12ab
printf("%04X", 4779); // gives 12AB
The "0x" counts towards the eight character count. You need "%#010x"
.
Note that #
does not append the 0x to 0 - the result will be 0000000000
- so you probably actually should just use "0x%08x"
anyway.
The %#08X
conversion must precede the value with 0X
; that is required by the standard. There's no evidence in the standard that the #
should alter the behaviour of the 08
part of the specification except that the 0X
prefix is counted as part of the length (so you might want/need to use %#010X
. If, like me, you like your hex presented as 0x1234CDEF
, then you have to use 0x%08X
to achieve the desired result. You could use %#.8X
and that should also insert the leading zeroes.
Try variations on the following code:
#include <stdio.h>
int main(void)
{
int j = 0;
printf("0x%.8X = %#08X = %#.8X = %#010x\n", j, j, j, j);
for (int i = 0; i < 8; i++)
{
j = (j << 4) | (i + 6);
printf("0x%.8X = %#08X = %#.8X = %#010x\n", j, j, j, j);
}
return(0);
}
On an RHEL 5 machine, and also on Mac OS X v10.7.5 (Lion), the output was:
0x00000000 = 00000000 = 00000000 = 0000000000
0x00000006 = 0X000006 = 0X00000006 = 0x00000006
0x00000067 = 0X000067 = 0X00000067 = 0x00000067
0x00000678 = 0X000678 = 0X00000678 = 0x00000678
0x00006789 = 0X006789 = 0X00006789 = 0x00006789
0x0006789A = 0X06789A = 0X0006789A = 0x0006789a
0x006789AB = 0X6789AB = 0X006789AB = 0x006789ab
0x06789ABC = 0X6789ABC = 0X06789ABC = 0x06789abc
0x6789ABCD = 0X6789ABCD = 0X6789ABCD = 0x6789abcd
I'm a little surprised at the treatment of 0; I'm not clear why the 0X
prefix is omitted, but with two separate systems doing it, it must be standard. It confirms my prejudices against the #
option.
The treatment of zero is according to the standard.
ISO/IEC 9899:2011 §7.21.6.1 The
fprintf
function
¶6 The flag characters and their meanings are: ...
#
The result is converted to an "alternative form". ... Forx
(orX
) conversion, a nonzero result has0x
(or0X
) prefixed to it. ...
(Emphasis added.)
Note that using %#X
will use upper-case letters for the hex digits and 0X
as the prefix; using %#x
will use lower-case letters for the hex digits and 0x
as the prefix. If you prefer 0x
as the prefix and upper-case letters, you have to code the 0x
separately: 0x%X
. Other format modifiers can be added as needed, of course.
For printing addresses, use the <inttypes.h>
header and the uintptr_t
type and the PRIXPTR
format macro:
#include <inttypes.h>
#include <stdio.h>
int main(void)
{
void *address = &address; // &address has type void ** but it converts to void *
printf("Address 0x%.12" PRIXPTR "\n", (uintptr_t)address);
return 0;
}
Example output:
Address 0x7FFEE5B29428
Choose your poison on the length — I find that a precision of 12 works well for addresses on a Mac running macOS. Combined with the .
to specify the minimum precision (digits), it formats addresses reliably. If you set the precision to 16, the extra 4 digits are always 0 in my experience on the Mac, but there's certainly a case to be made for using 16 instead of 12 in portable 64-bit code (but you'd use 8 for 32-bit code).
#
causes 0x
(or 0X
for %#X
) to be prepended to the output unless the value is 0
, so you should not use #
if you want 0x
to always appear in the output.
You can use the width field combined with the 0
flag to produce leading zeroes: %08x
pads the number with leading zeroes to a width of 8
. If you want consistent output for all 32-bit values, use "0x08x"
.
You could also use the precision field: %.8x
pads the number with leading zeroes to a total of 8
digits. Hence you can also use "0x%.8x"
for your purpose.
These conversion specifications would differ if a prefix is generated as part of the conversion, such as 0x
for #
or -
for negative numbers in signed conversions, whose length is counted for the width but not for the precision specifier. Furthermore, the precision field can be combined with the width field:
printf("|%10x|", 256) // outputs | 100|
printf("|%010x|", 256) // outputs |0000000100|
printf("|%#010x|", 256) // outputs |0x00000100|
printf("|%10.8x|", 256) // outputs | 00000100|
printf("|%#10.8x|", 256) // outputs |0x00000100|
printf("|0x%.8x|", 256) // outputs |0x00000100|
printf("|%10x|", 0) // outputs | 0|
printf("|%010x|", 0) // outputs |0000000000|
printf("|%#010x|", 0) // outputs |0000000000|
printf("|%10.8x|", 0) // outputs | 00000000|
printf("|%#10.8x|", 0) // outputs | 00000000|
printf("|0x%.8x|", 0) // outputs |0x00000000|
I would recommend using the last one: "0x%.8x"
.