2

我正在查看“C 陷阱和陷阱” ( PDF ) 第 5 章中给出的示例:

#include <stdio.h>

main()
{
    int c;

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

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

不幸的是,这个程序是错误的,原因很微妙。要查看问题出在哪里,请询问最后一次刷新缓冲区的时间。

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

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

我觉得很难理解。谁能详细解释一下?提前致谢。

4

2 回答 2

2

这意味着缓冲区只能从它进入范围的地方(定义它的地方)到它超出范围的地方在 final 处可用}

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

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

现在它似乎可以正常工作,如果它使用退出后未用于其他任何东西的堆栈的一部分,但它可能main无法正常工作的事实使它成为一个好主意。

尽管您引用的那本书已经过时了,但即使在当前的 C11 标准中,这仍然是一个问题。在详述setbuf和的部分中setvbuf

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

于 2012-05-31T02:21:33.357 回答
2

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

稍后,当main返回时,它的堆栈帧(包括 的内存buf)从堆栈中删除——但stdout仍在使用该地址的内存,因为这是您告诉它要做的。问题是,内存现在可以被其他人重用——你的 C 运行时的关闭代码、中断等等——并且在 C 运行时开始刷新stdout缓冲区之前,内存很可能会被其他一些数据覆盖。

(就个人而言,我不会使用术语“释放”来指代返回buf时会发生什么main——“释放”一词通常指的是堆分配,而不是堆栈分配。但是无论你怎么称呼它,当main返回时,buf内存就是不再是你可以放弃的。)

有关该问题的更生动的说明,请参阅Eric Lippert关于在其范围之外使用局部变量的内存的答案。这与您的示例中的问题相同。

于 2012-05-31T02:36:53.663 回答