7

所以我一直在研究 libc 的 stdio 部分是如何实现的,我遇到了另一个问题。看着man setvbuf我看到以下内容:

当文件上发生第一次 I/O 操作时,会调用 malloc(3),并获得一个缓冲区。

malloc这是有道理的,除非您实际使用它,否则您的程序不应包含I/O。我对此的直觉反应是 libc 将在这里清理自己的烂摊子。我只能假设它确实如此,因为 valgrind 报告没有内存泄漏(他们当然可以做一些肮脏的事情而不是malloc直接分配它......但我们假设它现在确实使用malloc)。

但是,您也可以指定自己的缓冲区...

int main() {
    char *p = malloc(100);
    setvbuf(stdio, p, _IOFBF, 100);
    puts("hello world");
}

哦不,内存泄漏!valgrind 证实了这一点。所以似乎每当 stdio 自己分配一个缓冲区时,它都会被自动删除(最迟在程序退出时,但可能在流关闭时)。但是,如果您明确指定缓冲区,那么您必须自己清理它。

不过有一个问题。手册页还说:

您必须确保关闭时间流时 buf 指向的空间仍然存在,这也发生在程序终止时。例如,以下内容无效:

现在这对标准流变得有趣了。由于它们在程序终止时关闭,如何正确清理为它们手动分配的缓冲区?我可以想象文件结构中的“当我关闭标志时清理它”,但它会变得毛茸茸,因为如果我读到这个权利做这样的事情:

setvbuf(stdout, 0, _IOFBF, 0);
printf("hello ");
setvbuf(stdout, 0, _IOLBF, 0);
printf("world\n");

由于这句话,将导致标准库进行 2 次分配:

如果参数 buf 为 NULL,则仅影响模式;将在下一次读取或写入操作时分配一个新缓冲区。

编辑:我的问题的附录。由于很明显我必须free传递任何缓冲区setvbuf,如果我确实使用它,stdout有什么实用的方法free吗?它必须活到程序结束。我能想到的最好的办法是fclose(stdout)释放它或使用一些人提到的静态缓冲区。我问是因为这看起来确实是一个尴尬的设计决定。

4

3 回答 3

3

同样来自手册页(至少在我的系统上):

如果 buf 不为 NULL,则调用者有责任在关闭流后释放(3)此缓冲区。

也就是说,你 malloc-ed 它,你释放它。

在退出之前,您可以自己关闭流,从而释放缓冲区。或者,您可以刷新流并setvbuf使用缓冲区参数再次NULL调用以切换回库管理的缓冲区或无缓冲 I/O。

于 2010-04-19T21:48:14.250 回答
3

至少根据 C 标准,您的最后一个场景根本是不允许的:“setvbuf 函数只能在 stream 指向的流与打开的文件关联之后和任何其他操作之前使用(不成功的调用除外) to setvbuf) 在流上执行。” (C99,§7.19.5.6/2)。

至于在更简单的情况下何时释放内存,方法是调用atexit()注册一个回调,该回调将在退出后释放内存main(),但在控制权返回给操作系统之前。

于 2010-04-19T22:02:43.877 回答
1

您可以关闭stdinstdoutstderr明确地(使用fclose())。

对于大多数操作系统,堆内存会在程序退出时自动释放。因此,未释放的缓冲区没有实际问题(有一个外观问题,即那些未释放的缓冲区会污染 Valgrind 的输出)。setvbuf()如果您有调用标准输入或输出的冲动,我的建议是使用静态缓冲区。静态缓冲区不需要分配或释放,这里很合适,因为只有三个标准流,并且您担心这些流对程序终止保持开放的情况。

于 2010-04-19T21:53:20.933 回答