2

In C, puts(string); will print string to stdout, followed by a newline. fputs(fileptr, string);, on the other hand, will write string to fileptr without the trailing newline. Is there any function like fputs() that appends a newline, or should I stick with fprintf(fileptr, "%s\n", string); like I've been using?

fputs() seems more efficient than fprintf() to me as it doesn't parse it's input. I know I could also use

fputs(string, fileptr);
fputc('\n', fileptr);

but I was wondering if there was a way to do that with one disk write.

I tried to figure out how puts() appends the newline (since printf() is really just a wrapper for vfprintf(stdout, ...), I thought the same might hold true for puts() and fputs()), but I oddly cannot find puts() in the glibc source.

Billy
  • 1,177
  • 2
  • 15
  • 35
  • *seems more efficient than ...* what profiling have you done that shows a significant difference in the two that is unacceptable and why? Especially since the disk I/O, even with buffering, is orders and orders of magnitude longer than cpu cycles? –  Jul 11 '18 at 03:44
  • Both those function calls (`fputs(fileptr, string);` and `fputc(fileptr, '\n');`) are wrong. The file stream parameter comes last, except for `fprintf()` and `fscanf()` and their variants. The function calls should be `fputs(string, fileptr);` and `fputc('\n', fileptr);`, therefore. – Jonathan Leffler Jul 11 '18 at 04:49

2 Answers2

8

I was wondering if there was a way to do that with one disk write.

You're over-optimizing things. The writing to disk is buffered, unless you happened to mess with the buffering settings or you're using a really bad libc implementation. In general, the best way to achieve what you want is

fputs(string, fileptr);
fputc('\n', fileptr);

hands down. If you wouldn't care about optimization, or you believe your compiler would optimize it, you can use

fprintf(fileptr, "%s\n", string);

it would need to parse the format string - and even then, internally, use the equivalent of fputs to write the %s and fputc to print the newline.


However there is one gotcha - be aware that the FILE structure usually has some kind of locking for thread safety. fputs + fputc would require acquiring the lock twice, whereas fprintf would probably do it just once. In a single-threaded application the lock wouldn't be contested, however. But it might just be that for a complicated format and multithreaded program, the fprintf parsing is faster than acquiring the locks for separate operations. Another is that a call from another thread can interleave the fputs/fputc, but fprintf is supposed to be atomic.


P.S. the puts code of Glibc is in libio/ioputs.c.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Is that better than fprintf(fileptr, "%s\n", string);? – Billy Jul 11 '18 at 03:45
  • You are way way down in the weeds here @Billy. A good compiler will likely optimize both to be identical. This is implementation defined, but should be close among compilers. The point being made about buffering is that the I/O operations read and write from a `BUFSIZ` buffer (see: C-library source `IO_BUFSIZ` (generally 8192 on Linux and 512 on windoze). So from an I/O standpoint it will be a basic wash. The ideological difference is that `fprintf` is a *variadic* function that generally has additional overhead compared to a function with fixed parameters - but optimization handles most. – David C. Rankin Jul 11 '18 at 03:52
  • There is the link I was looking for, it's now under [_G_BUFSIZ in Gnu libc (_G_config.h)](https://github.com/lattera/glibc/blob/master/sysdeps/gnu/_G_config.h) (with `_IO_BUFSIZ` defined as `_G_BUFSIZ` and `BUFSIZ` defined as `_IO_BUFSIZ` in separate header files) – David C. Rankin Jul 11 '18 at 03:59
  • @DavidC.Rankin Thanks for the writeup. You seem to be write, I ran `strings` on my program after compiling with `fprintf(fileptr, "%s\n", string);`, and I didn't see any occurences of `%s`. – Billy Jul 11 '18 at 04:02
  • Easier just to dump to assembly and look at it that way. `gcc -S -O2 -masm=intel -o yourfile.asm yourfile.c` to dump `yourfile.c` to `yourfile.asm` (using intel syntax which I find easier than ATT -- up to you). You will likely find your actual output reduced to a system call to `write`. (you can play with the optimization levels `-O0, -O1, -O2, -O3, -Ofast` and see how optimizations effect the code generation. (and being *way way down in the weed* isn't bad, but you generally end up either profiling, dumping assembly, or at the C-Standard or library source-code to get a final answer) – David C. Rankin Jul 11 '18 at 04:06
  • 1
    If you need to group together operations on a single file stream, a POSIX application can use [`flockfile()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html) and `funlockfile()` and `ftrylockfile()`. They're designed to be used so that `flockfile(fp); fprintf(fp, "…", …); fprintf(fp, "…", …); funlockfile(fp);` keeps the output from the two `fprintf()` operations consecutive without a chance for another thread to interpose its output. – Jonathan Leffler Jul 11 '18 at 04:55
1

Nothing will be more effective than :

  1. Make sure that your string can accommodate one char more
  2. Assign \n to the string
  3. fputs the string
Chris Tang
  • 567
  • 7
  • 18
0___________
  • 60,014
  • 4
  • 34
  • 74