-2

I'm trying to discover all capabilities of printf and I have tried this :

printf("Test:%+*0d", 10, 20);

that prints

Test:%+100d

I have use first the flag +, then the width * and the re-use the flag 0.

Why it's make this output ? I purposely used printf() in a bad way but I wonder why it shows me the number 100?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Sadek
  • 175
  • 1
  • 7
  • 4
    "I purposely use printf in a bad way" and thus you get UB, anything may happen. – Deduplicator Nov 13 '16 at 14:48
  • @Deduplicator Right, but I have to recode a function that reproduce the behaviour of printf – Sadek Nov 13 '16 at 14:51
  • Do you have to implement the undefined behavior of printf, or just the defined behavior? Hopefully just the defined behavior. (If not, you've got an impossible task, because different implementations of printf implement undefined behavior differently; which will you mimic?) In this case, the problem (that I'm hoping you don't have to worry about) is that the `0` flag follows the width specifier `*`. – Steve Summit Nov 13 '16 at 14:55
  • What is the _bad way_ you mentioned? Are you actually aware of the mistake? – Sourav Ghosh Nov 13 '16 at 14:55
  • `printf("Test:%+*0d", 10, 20);` --> `printf("Test:%+*.0d", 10, 20);` – Sourav Ghosh Nov 13 '16 at 14:56
  • 1
    It's likely not your duty to replicate the exact behavior of your implementation in the presence of ill-formed input. So simply forget about the precise behavior that you observe and focus on the results of well-formed input. – cmaster - reinstate monica Nov 13 '16 at 14:59

3 Answers3

2

This is because, you're supplying syntactical nonsense to the compiler, so it is free to do whatever it wants. Related reading, undefined behavior.

Compile your code with warnings enabled and it will tell you something like

warning: unknown conversion type character ‘0’ in format [-Wformat=]
printf("Test:%+*0d", 10, 20);
^

To be correct, the statement should be either of

  • printf("Test:%+*.0d", 10, 20); // note the '.'

    where, the 0 is used as a precision

    Related, quoting the C11, chapter §7.21.6.1, (emphasis mine)

    An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversions, the number of digits to appear after the decimal-point character for a, A, e, E, f, and F conversions, the maximum number of significant digits for the g and G conversions, or the maximum number of bytes to be written for s conversions. The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional decimal integer; if only the period is specified, the precision is taken as zero. If a precision appears with any other conversion specifier, the behavior is undefined.

  • printf("Test:%+0*d", 10, 20);

    where, the 0 is used as a flag. As per the syntax, all the flags should appear together, before any other conversion specification entry, you cannot just put it anywhere in the conversion specification and expect the compiler to follow your intention.

    Again, to quote, (and my emphasis)

    Each conversion specification is introduced by the character %. After the %, the following appear in sequence:

    • Zero or more flags (in any order) [...]
    • An optional minimum field width [...]
    • An optional precision [...]
    • An optional length modifier [...]
    • A conversion specifier [....]
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Great. I would suggest an *explicit* mention of [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) because many newbies don't grasp that notion. – Basile Starynkevitch Nov 13 '16 at 15:01
  • 1
    Hmm, no, he's not supplying nonsense to the compiler and it is not free to do something arbitrary. The printf() function implementation can however do whatever it wants with that string. – Hans Passant Nov 13 '16 at 15:01
  • @HansPassant: you are sort-of right. But a conforming compiler can (and often does, e.g. at least [GCC](http://gcc.gnu.org/)) handle `printf` specifically. – Basile Starynkevitch Nov 13 '16 at 15:02
  • 1
    @HansPassant From the point of the standard, it's UB. Individual implementations may have fixed behaviour for each scenario, but the standard does not mention anything specificc , at all, not even implementation defined behaviour. Please correct me if i'm wrong. – Sourav Ghosh Nov 13 '16 at 15:06
  • @HansPassant From the point of the standard, it's UB. Individual implementations may have fixed behaviour for each scenario, but the standard does not mention anything specfic , at all, not even implementation defined nehaviour. Please correct me if i'm wrong. – Sourav Ghosh Nov 13 '16 at 15:07
  • Can anybody mention the reson behind the downvote? What did I miss or misunderstood? – Sourav Ghosh Nov 13 '16 at 15:08
  • @chqrlie Sir, the problem with that is, the _intention_ maybe valid but the syntax is wrong. That is what I tried to point out, but I see your point, too. Let me edit to clarify that. thanks. :) – Sourav Ghosh Nov 13 '16 at 15:43
  • @chqrlie any better now? – Sourav Ghosh Nov 13 '16 at 15:53
  • @SouravGhosh: yes, much better, btw I am not the downvoter. Yet I wonder what the output should be for `%+.0d` for the value `0`. A single `+` character I suppose. – chqrlie Nov 13 '16 at 15:55
  • @chqrlie well, isn;t it _minimum_? – Sourav Ghosh Nov 13 '16 at 15:56
  • @chqrlie I know sir you're not the downvoter, then before me asking i would have received a constructive comment, I know that much. :) These are mostly pass-by downvotes, none of the ones who actually bothered to leave comment and engage into positive discussion. Asked just to be sure that I wasn't missing something obvious, but never matters much, anyway. :) – Sourav Ghosh Nov 13 '16 at 15:58
1

In complement of Sourav Ghosh's answer; an important notion is that of undefined behavior, which is tricky. Be sure to read Lattner's blog: What Every C Programmer Should Know About Undefined Behavior. See also this.

So, leaving on purpose (or perhaps depending upon) some undefined behavior in your code is intentional malpractice. Don't do that. In the very rare cases you want to do that (I cannot see any), please document it and justify yourself in some comment.

Be aware that if indeed printf is implemented by the C standard library, it can be (and often is) specially handled by the compiler (with GCC and GNU libc, that magic might happens using internally __builtin_printf)

The C99 & C11 standards are partially specifying the behavior of printf but does leave some undefined behavior cases to ease the implementation. You are unlikely to full understand or be able to mimic these cases. And the implementation itself could change (for example, on my Debian Linux, an upgrade of libc might change the undefined behavior of printf)

If you want to understand more printf study the source of some C standard library implementation (e.g. musl-libc, whose code is quite readable) and of the GCC implementation (assuming a Linux operating system).

But maintainers of GNU libc and of GCC (& even of the Linux kernel, thru syscalls) stay free to change the undefined behavior (of printf and anything else)

In practice, always compile with gcc -Wall (and probably also -g) if using GCC. Don't accept any warnings (so improve your own code till you get none).

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

Your printf format is incorrect: the flags must precede the width specifier.

After it handles * as the width specifier, printf expects either a . or a length modifier or a conversion specifier, 0 being none of these, the behavior is undefined.

Your library's implementation of printf does something bizarre, it seems to handle * by replacing it with the actual width argument... A side effect of the implementation. Others may do something else, including aborting the program. Such a format error would be especially risky if followed by a %s conversion.

Changing your code to printf("Test:%+0*d", 10, 20); should produce the expected output:

Test:+000000020
chqrlie
  • 131,814
  • 10
  • 121
  • 189