Search code examples
cprintfstdoutfputs

What is the right way of printing non formatted string not ending in new line?


printf("foo: "); 

vs

fputs("foo: ", stdout);

As mentioned in the title, the string is not formatted and does not end in a new line (so no puts). I am not sure about overheads and optimizations, so what is the rule of thumb is this case?

EDIT: Is it normal practice to use printf with non-formatted strings?


Solution

  • What is the right way

    Both ways are "right", both will work the same and fine.

    Consider rules of optimization. Don't optimize, if you don't have to. printf("foo: "); is completely fine, readable, understandable, everyone knows what it means. Keep it. printf("foo :"); is great.

    If you really want to optimize, and you really care about speed, you could call fwrite with statically calculated values:

    fwrite("foo: ", 1, sizeof("foo: ") - 1, stdout);
    

    But I doubt that optimization is worth anything. If you would really care about speed, you would call OS specific functions anyway, like on Linux we have write or splice or tee or vmsplice or copy_file_range or sendfile.

    if it is normal practice to use printf for non-formatted strings

    Let's take some big project and check. Analyzing gcc source code, there are that many lines matching fputs with a literal string:

    $ rg -g '*.[ch]' 'fputs\s*\(\s*"[^"]*"' gcc/ | wc -l
    263
    $ rg -g '*.[ch]' 'fputs\s*\(\s*"[^"]*"' gcc/ | head
    gcc/defaults.h:    fputs (":\n", (FILE));                       \
    gcc/defaults.h:    fputs (":\n", (FILE));                       \
    gcc/defaults.h:      fputs (", ", STREAM);                              \
    gcc/defaults.h:      fputs (", .-", STREAM);                            \
    gcc/config/csky/csky.h:      fputs ("\t.comm\t", STREAM);                               \
    gcc/config/csky/csky.h:  fputs ("\t.bss\t", (STREAM));                                  \
    gcc/config/xtensa/xtensa.h:    fputs ("@pcrel", FILE);                                          \
    gcc/config/c6x/c6x.h:       fputs ("\t.long ", FILE);                   \
    gcc/config/visium/visium.h:  do { assemble_name (STREAM, NAME); fputs (":\n", STREAM); } while (0)
    gcc/config/visium/visium.h:( fputs ("\n\t.comm  ", (STREAM)),     
    

    And that many lines matching printf with literal string and no arguments:

    $ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' gcc/ | wc -l
    1792
    

    There are that many lines with printf that have no newlines in formatting string:

    $ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' gcc/ | grep -v '"[^"]*\\n[^"]*"' | wc -l
    470
    $ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' | grep -v '"[^"]*\\n[^"]*"' | head
    zlib/contrib/minizip/miniunz.c:          printf(" ");
    libdecnumber/decNumber.c:    if (decNumberIsInfinite(dn)) printf("Infinity");
    libdecnumber/decNumber.c:      if (dn->bits&DECSNAN) printf("sNaN");  /* signalling NaN */
    libdecnumber/decNumber.c:       else printf("NaN");
    libdecnumber/decNumber.c:    printf(" ");
    libdecnumber/decNumber.c:    printf(":");
    libdecnumber/decCommon.c:       if ((num->lsd-ub)%3==0 && ub!=num->lsd) printf(" "); /* 4-space */
    libdecnumber/decBasic.c:  printf("----- div=");
    libdecnumber/decBasic.c:  printf("DivQuo:");
    libdecnumber/decBasic.c:    printf("CoeffbL:");
    

    These numbers look high enough, I would say any is "right". Looking at it, there are even more printf("literal") than fputs("literal", anything).