我正在查看"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只是一个必须清理堆栈和变量的函数。但这是什么意思:缓冲区已被释放?
我觉得很难理解。任何人都可以解释一下吗?提前谢谢。
答案 0 :(得分:2)
这意味着缓冲区只能从它进入范围(在其定义的位置)到最终}
范围之外的范围内使用。
之后对缓冲区的任何使用都是未定义的行为。标准输出的刷新必然会使用缓冲区,因为它是缓冲输出字符的地方。
因此存在问题 - 在刷新之前,有可能重新使用缓冲区的内存区域(取决于C运行时环境关闭代码的复杂程度)。
现在它可能似乎工作正常,如果它使用了main
退出后未用于其他任何内容的堆栈的一部分,但事实上可能无法正常工作这是一个好主意。
虽然您引用的那本书非常陈旧,但即使在当前的C11标准中,这仍然是的问题。详细介绍了setbuf
和setvbuf
:
缓冲区的生命周期至少与开放流一样长,因此在阻止退出时释放具有自动存储持续时间的缓冲区之前,应关闭该流。
答案 1 :(得分:2)
您正在堆栈上分配一个缓冲区,然后调用setbuf
说“从现在开始使用此缓冲区stdout
”。
稍后,当main
返回时,其堆栈帧(包括buf
的内存)将从堆栈中删除 - 但stdout
仍然在该地址使用内存,因为那是你告诉它要做什么。问题是,内存现在可以被其他人重用 - 你的C运行时的关闭代码,一个中断,无论如何 - 在C运行时转换为{{1}之前,内存可能会被其他一些数据覆盖的缓冲区。
(就我个人而言,我不会使用术语“释放”来指代stdout
返回时buf
会发生什么 - “free”这个词通常指的是堆分配,而不是堆栈分配但是无论你怎么称呼它,当main
返回时,main
就是你不再愿意放弃的记忆。)
有关问题的更生动的说明,请参阅Eric Lippert's answer关于在其范围之外使用局部变量的内存。这与你的例子中的问题相同。