为什么这个缓冲输出和内存分配的例子存在缺陷?

时间:2012-05-31 02:15:00

标签: c

我正在查看"C Traps and Pitfalls"PDF)第5章中给出的示例:

#include <stdio.h>

main()
{
    int c;

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

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

不幸的是,由于一个微妙的原因,这个程序是错误的。查看   麻烦在哪里,问一下最后一次冲洗缓冲区的时间   时间。

     

答:主程序完成后,作为其中的一部分   在将控制权交还给图书馆之前清理图书馆   操作系统。但到那时,缓冲区已经被释放了!

main只是一个必须清理堆栈和变量的函数。但这是什么意思:缓冲区已被释放?

我觉得很难理解。任何人都可以解释一下吗?提前谢谢。

2 个答案:

答案 0 :(得分:2)

这意味着缓冲区只能从它进入范围(在其定义的位置)到最终}范围之外的范围内使用。

之后对缓冲区的任何使用都是未定义的行为。标准输出的刷新必然会使用缓冲区,因为它是缓冲输出字符的地方。

因此存在问题 - 在刷新之前,有可能重新使用缓冲区的内存区域(取决于C运行时环境关闭代码的复杂程度)。

现在它可能似乎工作正常,如果它使用了main退出后未用于其他任何内容的堆栈的一部分,但事实上可能无法正常工作这是一个好主意。

虽然您引用的那本书非常陈旧,但即使在当前的C11标准中,这仍然是的问题。详细介绍了setbufsetvbuf

  

缓冲区的生命周期至少与开放流一样长,因此在阻止退出时释放具有自动存储持续时间的缓冲区之前,应关闭该流。

答案 1 :(得分:2)

您正在堆栈上分配一个缓冲区,然后调用setbuf说“从现在开始使用此缓冲区stdout”。

稍后,当main返回时,其堆栈帧(包括buf的内存)将从堆栈中删除 - 但stdout仍然在该地址使用内存,因为那是你告诉它要做什么。问题是,内存现在可以被其他人重用 - 你的C运行时的关闭代码,一个中断,无论如何 - 在C运行时转换为{{1}之前,内存可能会被其他一些数据覆盖的缓冲区。

(就我个人而言,我不会使用术语“释放”来指代stdout返回时buf会发生什么 - “free”这个词通常指的是堆分配,而不是堆栈分配但是无论你怎么称呼它,当main返回时,main就是你不再愿意放弃的记忆。)

有关问题的更生动的说明,请参阅Eric Lippert's answer关于在其范围之外使用局部变量的内存。这与你的例子中的问题相同。