2

While reading the getc man page, I came across:

may be implemented as a macro which evaluates stream more than once.

So, getc might be implemented as a function as well - what exactly means by evaluates stream more than once?

Agrudge Amicus
  • 1,033
  • 7
  • 19
  • 2
    It means that since `getc` is a macro, it's `stream` parameter may be expanded multiple times wherever it's used inside the macro. If you don't want this behavior, use `fgetc`. – mediocrevegetable1 Sep 24 '21 at 13:27
  • @mediocrevegetable1 Can you provide any reference to look at `getc`'s macro implementation? – Agrudge Amicus Sep 24 '21 at 13:28
  • You could check the standard library's source code on your own system and see `getc`'s implementation. Though it may not be implemented as a macro. It's allowed to be, but it's not a requirement. – mediocrevegetable1 Sep 24 '21 at 13:31

4 Answers4

9

Consider a function:

FILE *getstream(void);

And a code:

int c = getc(getstream());

In case getc is a function, getstream will be called only once, its result will be stored in a temporary variable/register and passed to getc.

In case getc is a macro, let say something like:

#define getc(s)  ((s) == NULL) ? -1 :_getc(s))

it will get expanded into:

int c = (((getstream()) == NULL) ? -1 :_getc(getstream()));

That is it will call getstream() twice. In case getstream has some side effects (using IO, modifying some global state), the flow of the program will be different between the two cases and one must be aware of this.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
  • However I suppose it is rather uncommon to call `getc` with a parameter whose multiple evaluation may cause problems. – Jabberwocky Sep 24 '21 at 13:33
  • @Jabberwocky I'd say it should not be done at all, exactly for this reason :) – Eugene Sh. Sep 24 '21 at 13:34
  • Exactly, otherwise calling `getc(getstream())` in a loop may also trigger problems. – Jabberwocky Sep 24 '21 at 13:35
  • @EugeneSh. isn't this entire case valid for any macro vs unction situation? – Agrudge Amicus Sep 24 '21 at 13:36
  • 1
    @AgrudgeAmicus Yes, of course. When dealing with macros one should be fully aware of possible pitfalls (and there is a bunch) – Eugene Sh. Sep 24 '21 at 13:38
  • 1
    @AgrudgeAmicus: Yes, but the point of warning you about it in the documentation for `getc` is that people may think of the standard library facilities as functions and so forget about the possibility they are macros that may evaluate their arguments multiple times. – Eric Postpischil Sep 24 '21 at 13:39
4

This means that the expression passed to getc as an argument could be evaluated multiple times. So if this expression contains an assignment, increment/decrement, or function call, it will happen more than once.

For example, given this macro:

#define PRINT_INT(x) printf("%d (%x)\n", (x), (x))

This code:

int x = 3;
PRINT_INT(++x);

Will expand to:

int x = 3;
printf("%d (%x)\n", (++x), (++x))

This would cause x to get incremented more than once without a sequence point and trigger undefined behavior.

The man page is saying that getc may be doing something similar, so don't pass anything to it that could have a side effect.

dbush
  • 205,898
  • 23
  • 218
  • 273
2

The short answer is that if you were to write something like

int c = getc(fp[i++]);

you would potentially run into big trouble, because the getc macro might evaluate its argument fp[i++] twice, meaning not only that i would potentially get bumped by 2, but also because (in the absence of sequence points) you'd descend all the way into formally undefined behavior.

But if you never call getc on an argument with a side effect, it's perfectly safe.

For some reason, I see people calling fgetc more and more often these days. Presumably they're being taught this because it's "safer", although to my mind, it's a pretty unnecessary concern in the grand scheme of things.

See also getc() vs fgetc() - What are the major differences?.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
1

You must not suppose that getc's argument is evaluated conforming to a function-call discipline (call-by-value in this case).

This comes from the paragraph "7.19.7.5 The getc function" the ISO 9899 that defines the C language:

The getc function is equivalent to fgetc, except that if it is implemented as a macro, it may evaluate stream more than once, so the argument should never be an expression with side effects

The definitions of the libc functions that you find in the linux man pages come from a combination of sources that vary between the ISO9899, POSIX, etc (see the CONFORMING TO paragraphs from each manual to see the origin of the definitions).

alinsoar
  • 15,386
  • 4
  • 57
  • 74