1

"Advanced Programming in the UNIX Environment, 3rd Edition", page 151:

The difference between getc and fgetc is that getc can be implemented as a macro, whereas fgetc cannot be implemented as a macro. This means three things:

  • The argument to getc should not be an expression with side effects.
  • Since fgetc is guaranteed to be a function, we can take its address. This allows us to pass the address of fgetc as an argument to another function.
  • Calls to fgetc probably take longer than calls to getc, as it usually takes more time to call a function.

What "expression with side effects" can occur for the function signatures with stream pointer as a parameter?

#include<stdio.h>
int getc(FILE* stream);
int fgetc(FILE* stream);
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Andrey Bushman
  • 11,712
  • 17
  • 87
  • 182

2 Answers2

6

There are probably hundreds of ways to pass an expression with side effects, but a "credible" one would be something like:

FILE *files[NUM_FILES];
...
int rc = getc(files[counter++]);

If getc is implemented poorly as a macro, the expression files[counter++] could be evaluated more than once, leading to unexpected behavior.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • 2
    "poorly"? "I doubt that any sane C library"? You have no idea what you're talking about. The original stdio library and many of its descendants implemented getc(f) as something like `--(f)->ptr < (f)->end? *(f)->ptr++ : __getc(f)` for speed, which is why getc is allowed to be a macro that reevaluates its argument. – Jim Balter Dec 17 '13 at 09:17
  • -1 re @JimBalter note about "If getc is implemented poorly as a macro, the expression files[counter++] could be evaluated more than once, leading to unexpected behavior. I doubt that any sane C library would make such an error though.". this is dangerous disinformation to pass around. – Cheers and hth. - Alf Dec 17 '13 at 09:48
  • Make that `--(f)->cnt > 0? *(f)->ptr++ : __getc(f)` – Jim Balter Dec 18 '13 at 19:45
1

As an example, don't write

FILE* foo() { puts( "Bah!\n" ); return stdout; }

void advance() { getc( foo() ); }
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @Bush: absence of guarantee = possibility of malf, not guaranteed malf – Cheers and hth. - Alf Dec 17 '13 at 11:17
  • @Bush How is your response at all relevant? You asked how an expression with side effects can be passed to getc, and this is clearly an example. The text you quoted says "getc can be implemented as a macro" -- I realize English isn't your native language, but surely you understand what "can" means? If you called advance and didn't get more than one "Bah!" printed, then the version of getc you're using isn't a macro. (You can check by looking at stdio.h or macro expanding your source, e.g., with gcc -E) – Jim Balter Dec 18 '13 at 19:51
  • Yes, the "Bah!" was printed once for my gcc (version 4.8.1). Thank you. – Andrey Bushman Dec 19 '13 at 04:35