-2

Consistency is fundamental property in a code base, let alone in a programming language that eventually turned out to be the most used in the world.

There are two families of I/O functions in C: formatted and unformatted. These are:

int fprintf(FILE *stream, const char *format, ...);
int vfprintf(FILE *stream, const char *format, va_list ap);

And

int fputc(int c, FILE *stream);
int fputs(const char *s, FILE *stream);
int putc(int c, FILE *stream);

*fprintf seems to go for the (where, what) ordering whereas *put* for (what, where); same holds for input functions. Why is the stream parameter at different positions? Are there any historical/design motivations for such choice?

edmz
  • 8,220
  • 2
  • 26
  • 45
  • 2
    Well for one, since the `printf` family takes a variable number of arguments, you couldn't stick a `FILE *stream` param at the end of it. – Colonel Thirty Two Nov 15 '16 at 21:01
  • @ColonelThirtyTwo: It could have `fprintf(const char *format, FILE *stream, ...)` – edmz Nov 15 '16 at 21:03
  • 4
    Languages evolve. Not every quirk is the product of explicit design decision. To quote the preface of the 2nd edition of K&R: "C, like any other language, has its blemishes." – John Coleman Nov 15 '16 at 21:04
  • 2
    Which would be inconsistent with plain `printf`, which has the `format` and varargs next to each other. – Colonel Thirty Two Nov 15 '16 at 21:04
  • 2
    You can speculate (with @ColonelThirtyTwo 's observation about the variable number of arguments to `printf` a reasonable guess), but unless the person who wrote that part of the standard library wrote down their motivation somewhere, you are speculating about the motivation of a person who is quite possibly dead (or if alive, unlikely to remember -- I don't always recall some of my design decisions from just last year, let alone 30 years ago). – John Coleman Nov 15 '16 at 21:21
  • 1
    It's not clear what kind of an answer you're expecting here. If you're looking for someone to say that C's stdio had the dubious benefit of some of the worst library API design in the history of computer programming I'm happy to volunteer, but the discussion is futile. If you're looking for a hidden reason, good luck. – user207421 Nov 15 '16 at 21:31

1 Answers1

1

There is no good choice for fprintf family, because functions that take variable-argument lists need to take the "fixed" portion of their parameters before the variadic part:

  • Sticking stream in the middle, as in fprintf(const char *format, FILE *stream, ...) would be inconsistent with printf, where formatted values follow the format string immediately. It would stay inconsistent with fputc family, too.
  • Sticking stream into the variadic part, as in fprintf(const char *format, ...), is theoretically possible, but it would be inefficient, because you'd need to get to the last argument before you can start writing to your stream.

fputc family, on the other hand, could be rewritten as fputc(FILE *stream, int c). However, its precursor putc was mentioned in the original version of K&R book (pdf, scroll to page 152) so the authors of the standard decided to stick to the convention when adding the no-macro fputc function to the library.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • The good, and obvious, choice, would have been to put the `FILE *` *first* throughout `stdio`, but somebody had probably already screwed up by putting it last in `fread()/fwrite()`, so here we are. – user207421 Nov 15 '16 at 21:30
  • @EJP I did not find `fread`/`fwrite` in the first edition of K&R, only `putc` and `fputs` (no `fputc` or `puts`, though). That's why I think this is the origin of this screw-up. And yes, I completely agree with you that this is indeed a screw-up. – Sergey Kalinichenko Nov 15 '16 at 21:37