23

I was reading the C programming book by Kernighan and and came across putc and fputc. I didn't quite understand the difference between the two or when would one be used over the other. I found some posts on StackOverflow which dealt with this topic but it's still unclear.

As mentioned here (putc needs stdout, vs puts) :

  1. According to Kernighan's book putc is equivalent to fputc but putc could be implemented as a macro and putc may evaluate its stream argument more than once.

  2. The difference between putc and fputc is that by using putc, you risk running the macro version which is inherently unsafe because it may have to evaluate its stream argument more than once. This causes complications that most people aren't aware of and thus do not watch out for, so fputc is better to use. fputc's macro does not have this problem.

Questions:

  1. putc can be implemented as a macro but what is the problem doing same with fputc?

  2. The second statement mentions of some complications and safety issues. What are those?

  3. putc evaluates its argument more than once. So what are the advantages or disadvantages it poses compared to evaluating the argument.

Community
  • 1
  • 1
Vivek Rai
  • 890
  • 1
  • 9
  • 22
  • Regarding performance, as red [here](http://pic.dhe.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf1%2Fputc.htm): "The fputc subroutine works the same as the putc macro, but fputc is a true subroutine rather than a macro. It runs more slowly than putc, but takes less space per invocation." - – KrisWebDev Jun 29 '13 at 18:37

1 Answers1

27

The issue with a macro implementation is that if any of the arguments have side effects, those side effects may be evaluated more than once, possibly leading to undefined behavior. Consider this toy example:

#define SQUARE(x) ((x) * (x))

Most of the time, this will function as expected, but if you pass in an expression such as f(), then the side effect of calling the function f() will happen twice, not once, since the preprocessor is just a text transformer which is ignorant of C:

int f()
{
    printf("f() was called\n");
    return 42;
}
...
int x = SQUARE(f());  // This calls f() twice!  It gets expanded to this:
// int x = (f() * f());

To put this in perspective, the putc function, if implemented as a macro, may evaluate its stream argument more than once. So, if that stream comes from a function:

FILE *get_file()
{
    // Potential side effects could happen here
    return some_file;
}
...
putc('A', get_file());

Then that could result in the function get_file() getting called more than once, with potentially unwanted side effects.

The solution, of course, is to just call a regular function like fputc() instead of putc(). Since it's not a macro, it doesn't have any potential problems with evaluating its arguments more than once. Macros can be dangerous sometimes, so use them with care.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Thanks! That answers almost all of my queries. Now one specific query related to your post. Can someone explain me what do these 'side effects' means in a broader and generic sense? Thanks – Vivek Rai Dec 23 '12 at 06:20
  • 1
    Note that `putc` as a macro is only allowed to evaluate the file stream argument more than once; it is not allowed to evaluate the character argument more than once. Note too that there must be an actual function `putc` and that you can use it with `#undef putc` or `(putc)(c, fp)`. – Jonathan Leffler Dec 23 '12 at 06:20
  • 1
    Consider: `FILE *files[3]; ...open 3 files...; FILE **fpp; while ((c = *str++) != '\0') { fpp = files; for (int i = 0; i < 3; i++) putc(c, *fpp++);`. If `putc()` is a macro that evaluates its stream pointer more than once, then the `*fpp++` may be evaluated more than once, leading to chaos. If `putc()` is a function, or if you invoked `fputc()`, then `*fpp++` is guaranteed to be evaluated just once. It is a largely artificial example, but semi-plausible. – Jonathan Leffler Dec 23 '12 at 06:25
  • @JimBalter: No, your claim is baseless and erroneous—there **is** such a requirement. C99 §7.1.4/1 says "Any function declared in a header may be additionally implemented as a function-like macro defined in the header [...]", and §7.19.7.8/2 says "The `putc` function is equivalent to `fputc`, except that if it is implemented as a macro, it may evaluate `stream` more than once, so that argument should never be an expression with side effects." It's guaranteed to be a function, but it may *also* be a macro. – Adam Rosenfield Mar 17 '14 at 15:48
  • Those words don't provide such a requirement, they only say that functions may also be implemented as macros. However, I now agree that there is such a requirement -- not because of the irrelevant text you cited, but because 7.19.7.8 refers to the "putc function" and gives a prototype for the function. – Jim Balter Mar 17 '14 at 19:09
  • @JimBalter: §7.1.3/1 says "Each header declares or defines all identifiers listed in its associated subclause" and "All identifiers with external linkage in any of the following subclauses (including the future library directions) are always reserved for use as identifiers with external linkage.", and §7.19.7.8/1 gives a prototype for `putc()` with (implicit) external linkage. Therefore, there must be a function with external linkage named `putc()`. – Adam Rosenfield Mar 20 '14 at 16:01
  • 1
    I already agreed, and I deleted my original comment, so why are you still blabbing about this? – Jim Balter Mar 20 '14 at 20:22
  • Just make sure `putc` is a function, not a macro: ` (putc)(c,stdout);` – Chef Gladiator May 24 '21 at 07:14