Why printf is not able to handle flags, field width and precisions properly?

老子叫甜甜 提交于 2019-12-13 10:52:23

问题


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?


回答1:


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 [....]



回答2:


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



回答3:


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).



来源:https://stackoverflow.com/questions/40575096/why-printf-is-not-able-to-handle-flags-field-width-and-precisions-properly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!