所以我一直在深入研究如何实现libc的stdio部分,并且我遇到了另一个问题。查看man setvbuf
我看到以下内容:
发生第一次I / O操作时 一个文件,malloc(3)被调用,和一个 获得缓冲区。
这是有道理的,除非您实际使用I / O,否则您的程序中不应包含malloc
。我对此的直觉反应是,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)
然后释放它或使用静态缓冲区,正如有些人提到的那样。我问,因为这似乎是一个尴尬的设计决定。
答案 0 :(得分:3)
同样来自man page(至少在我的系统上):
如果buf不为NULL,则关闭流后,调用者有责任释放(3)此缓冲区。
也就是说,你把它解决了,你释放它。
退出之前,您可以自己关闭流,从而允许您释放缓冲区。或者,您可以刷新流并使用setvbuf
缓冲区参数再次调用NULL
以切换回库管理缓冲区或无缓冲I / O.
答案 1 :(得分:3)
至少根据C标准,您的最后一个场景是不允许的:“只有在stream指向的流与打开的文件关联之后以及任何其他操作之前,才可以使用setvbuf函数(除了在流上执行对setvbuf的不成功调用。“ (C99,§7.19.5.6/ 2)。
关于何时在更简单的情况下释放内存,在途中是调用atexit()
来注册一个回调,该回调将在退出main()
之后释放内存,但在控制返回到OS。
答案 2 :(得分:1)
您可以明确关闭stdin
,stdout
和stderr
(fclose()
)。
对于大多数操作系统,程序退出时会自动释放堆内存。因此,没有未释放的缓冲区没有实际问题(存在一个外观问题,即那些未发布的缓冲区会污染Valgrind的输出)。如果你觉得在标准输入或输出上调用setvbuf()
的冲动,我的建议是使用静态缓冲区。静态缓冲区不需要分配或释放,并且在这里是合适的,因为只有三个标准流,并且您担心这些流在程序终止时保持打开状态。