Why is the prefix written with octal when using both the #
flag and explicit 0
precision for unsigned int conversions? Is it normal?
I've noticed this and it feels like a bug (I'm not claiming it is).
However I'd like to understand how it works because it's making very little sense to me. Is there a rationale behind?
My best guess so far is that the prefix is handled after the precision. Since an empty output (which is what you get with printing 0 with an explicit precision of 0) doesn't start with "0" the "0" prefix is added. But I would've expected the prefix to be included in the concept of "output" and therefore I'd have expected it to not be displayed.
MRE:
/* main.c */
#include <stdio.h>
int main()
{
printf("%%u conv: [%#.0u]\n", 0);
printf("%%o conv: [%#.0o]\n", 0);
return (0);
}
Output:
> gcc -o test main.c && ./test
%u conv: []
%o conv: [0]
Highlights from man 3 printf
:
- On the
#
flag:
The value should be converted to an "alternate form". For o conversions, the first character of the output string is made zero (by prefixing a 0 if it was not zero already). For x and X conversions, a nonzero result has the string "0x" (or "0X" for X conversions) prepended to it. [...]
- On the precision:
An optional precision, in the form of a period ('.') followed by an optional decimal digit string. [...] This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions [...].
- On unsigned integers conversions (
u
,o
,x
,X
):
[...] The precision, if any, gives the minimum number of digits that must appear; if the converted value requires fewer digits, it is padded on the left with zeros. The default precision is 1. When 0 is printed with an explicit precision 0, the output is empty.
Why is the prefix written with octal when using both the #
flag and explicit 0
precision for unsigned int conversions? Is it normal?
I've noticed this and it feels like a bug (I'm not claiming it is).
However I'd like to understand how it works because it's making very little sense to me. Is there a rationale behind?
My best guess so far is that the prefix is handled after the precision. Since an empty output (which is what you get with printing 0 with an explicit precision of 0) doesn't start with "0" the "0" prefix is added. But I would've expected the prefix to be included in the concept of "output" and therefore I'd have expected it to not be displayed.
MRE:
/* main.c */
#include <stdio.h>
int main()
{
printf("%%u conv: [%#.0u]\n", 0);
printf("%%o conv: [%#.0o]\n", 0);
return (0);
}
Output:
> gcc -o test main.c && ./test
%u conv: []
%o conv: [0]
Highlights from man 3 printf
:
- On the
#
flag:
The value should be converted to an "alternate form". For o conversions, the first character of the output string is made zero (by prefixing a 0 if it was not zero already). For x and X conversions, a nonzero result has the string "0x" (or "0X" for X conversions) prepended to it. [...]
- On the precision:
An optional precision, in the form of a period ('.') followed by an optional decimal digit string. [...] This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions [...].
- On unsigned integers conversions (
u
,o
,x
,X
):
Share Improve this question edited Mar 20 at 2:19 vmonteco asked Mar 19 at 13:19 vmontecovmonteco 15.6k17 gold badges59 silver badges91 bronze badges 2 |[...] The precision, if any, gives the minimum number of digits that must appear; if the converted value requires fewer digits, it is padded on the left with zeros. The default precision is 1. When 0 is printed with an explicit precision 0, the output is empty.
2 Answers
Reset to default 5This is normal, and specified by the C standard:
#
The result is converted to an ''alternative form''. For o conversion, it increases the precision, if and only if necessary, to force the first digit of the result to be a zero (if the value and precision are both 0, a single 0 is printed). For x (or X) conversion, a nonzero result has 0x (or 0X) prefixed to it. ...
For o
conversion, #
forces a leading zero even if the value and precision are both 0. For x conversion, the leading 0x or 0X is only prefixed to nonzero values.
You can also see that the man page you quoted says something similar: #
with o
forces a leading zero no matter what, while #
with x
or X
only adds a prefix to nonzero values.
Note that o
, x
, and X
are the only integer conversions for which the behavior of #
is defined. Using #
with u
is undefined behavior.
It behaves as specified by ISO C23 7.23.6.2:
#
The result is converted to an "alternative form". For o conversion, it increases the precision, if and only if necessary, to force the first digit of the result to be a zero (if the value and precision are both 0, a single 0 is printed).
Furthermore, #
together with %d
or %u
is not specified, so %#.0u
is either undefined behavior or a non-standard extension.
d i o u x X
conversions print0
as[] [] [0] [] [] []
and1
as[1] [1] [01] [1] [0x1] [0x1]
(explicitly:[%#.0d] [%#.0i] [%#.0o] [%#.0u] [%#.0x] [%#.0X]
) – Wyck Commented Mar 19 at 13:39