2

I am looking at an example given in Chapter 5 of "C Traps and Pitfalls" (PDF):

#include <stdio.h>

main()
{
    int c;

    char buf[BUFSIZ];
    setbuf(stdout, buf);

    while ((c = getchar()) != EOF)
        putchar(c);
}

Unfortunately, this program is wrong, for a subtle reason. To see where the trouble lies, ask when the buffer is flushed for the last time.

Answer: after the main program has finished, as part of the cleaning up that the library does before handing control back to the operating system.But by that time, the buffer has already been freed!

main is just a function that must clean up stacks and variables. but what does it mean: the buffer has already been freed?

i find it's difficult to understand. could anyone explain it detaily? thanks in advance.

attach
  • 19
  • 2
  • It looks like you're quoting from a book or Web site. You should provide a link to your source, so that (a) people can see the context and (b) it doesn't come across as plagiarism. – Joe White May 31 '12 at 02:18
  • use malloc & don't free it so that when the OS cleans it up after main's termination, buffer will have valid data. – P.P May 31 '12 at 02:26
  • You don't need to get into complex allocation stuff, you can flush it manually or move it outside of `main`. – paxdiablo May 31 '12 at 02:29
  • BUFSIZ is some global constant? Jeez, old-school C sure is arcane stuff. – HostileFork says dont trust SE May 31 '12 at 03:00
  • @attach Take the suggestions in that text with a grain of salt; it documents K&R C, not standard C. – Dave May 31 '12 at 03:35
  • Please do not take that _particular_ suggestion with a grain of salt. C11 has exactly the same restriction in terms of the required scope for the buffer. – paxdiablo May 31 '12 at 04:14

2 Answers2

2

It means the buffer is only usable from where it comes in to scope (where it's defined) to the point where it goes out of scope, at the final }.

Any use of the buffer after that is undefined behaviour. The flushing of standard output would, by necessity, use that buffer, since that's where it's buffering the output characters.

Hence the problem - there's a possibility the memory area where the buffer was has been re-used before the flushing takes place (depending on how complex the C runtime environment shut-down code is).

Now it may seem to work okay, if it uses a part of the stack that's not used for anything else after main exits, but the fact that it may work in no way makes it a good idea.

Although that particular book you quote from is rather dated, this is still an issue even in the current C11 standard. In the sections detailing setbuf and setvbuf:

The buffer has to have a lifetime at least as great as the open stream, so the stream should be closed before a buffer that has automatic storage duration is deallocated upon block exit.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
2

You're allocating a buffer on the stack, and then calling setbuf to say "use this buffer for stdout from now on".

Later, when main returns, its stack frame (including the memory for buf) is removed from the stack -- but stdout is still using memory at that address, because that's what you told it to do. The problem is, that memory could now be reused by somebody else -- your C runtime's shutdown code, an interrupt, whatever -- and the memory might well get overwritten with some other data before the C runtime gets around to flushing stdout's buffer.

(Personally, I wouldn't use the terminology "freed" to refer to what happens to buf when main returns -- the word "free" usually refers to heap allocations, not stack allocations. But whatever you call it, when main returns, buf is memory that's no longer yours to give away.)

For a more vivid illustration of the problem, see Eric Lippert's answer about using a local variable's memory outside its scope. It's the same problem as in your example.

Community
  • 1
  • 1
Joe White
  • 94,807
  • 60
  • 220
  • 330