考虑这个代码:
#include <stdio.h>
int main()
{
char buffer[500];
int n = setvbuf(stdout, buffer, _IOFBF, 100);
printf("Hello");
while(1 == 1)
;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在 Linux 上运行时,“Hello”消息立即出现在输出设备上,然后程序无限期挂起。输出不应该被缓冲,直到stdout刷新或关闭,手动或在正常程序终止时?这就是 Windows 10 上发生的情况,如果缓冲区大小指定为 130 字节或更多,Linux 上也会发生这种情况。我在两个系统上都使用 VS Code。
我错过了什么?我对完整缓冲概念有什么误解吗?
我缺少什么?我对完全缓冲概念的理解有误吗?
你对这个概念没有错。正如 @WilliamPursell 在他的回答中所观察到的那样,规范语言的措辞存在回旋余地,但是根据规范的明确意图,您的程序观察到的行为并未表现出完全缓冲。此外,我将规范解释为尽管由于某种原因无法实现意图,但仍为实现留出了符合要求的空间,而不是为可以合理实现意图但随意做不同事情的实现提供免费通行证。
我在 Linux 上针对 Glibc 2.22 在您的程序上测试了这个变体:
#include <stdio.h>
int main() {
static char buffer[BUFSIZ] = { 0 };
int n = setvbuf(stdout, buffer, _IOFBF, 100);
if (n != 0) {
perror("setvbuf");
return 1;
}
printf("Hello");
puts(buffer);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
程序以状态0退出,并且没有打印任何错误输出,因此我得出结论,setvbuf返回0,表明成功。然而,程序只打印了一次“Hello”,表明它实际上没有使用指定的缓冲区。如果我将指定的缓冲区大小增加到setvbuf128 字节 (== 2 7 ),则输出为“HelloHello”,表明指定的缓冲区已被使用。
那么,观察到的行为似乎是1 ,当指定提供的缓冲区小于 128 字节时,此实现setvbuf会默默地将流设置为无缓冲。这也与您的程序版本的行为一致,但与我对函数规范的阅读不一致:
[...]该参数
mode确定如何stream缓冲,如下所示:_IOFBF导致输入/输出完全缓冲[...]。如果buf不是空指针,则可以使用它指向的数组代替函数分配的缓冲区setvbuf,并且参数size指定数组的大小;否则,size可以确定该函数分配的缓冲区的大小setvbuf。任何时候数组的内容都是不确定的。如果成功,该
setvbuf函数将返回零;如果给出的值无效mode或请求无法得到满足,则返回非零值。
(C17,7.21.5.6 /2-3)
当我阅读规范时,setvbuf可以自行决定是否使用指定的缓冲区,如果它选择不这样做,那么它可能会或可能不会使用指定大小的缓冲区,但它必须设置指定的缓冲模式或失败。将缓冲模式更改为与原始模式和请求的模式都不同的缓冲模式是不符合规范的,并且未能设置请求的模式但返回 0 也是不符合的。
由于我的结论是这个 Glibc 版本的setvbuf行为违反了语言规范,所以我想说你已经被 glibc bug 绊倒了。
1但需要注意的是,规范中说缓冲区的内容在任何时候都是不确定的。因此,通过在请求将缓冲区setvbuf分配为流缓冲区后访问该缓冲区,该程序会调用未定义的行为,因此,从技术上讲,它不能证明任何事情。
| 归档时间: |
|
| 查看次数: |
100 次 |
| 最近记录: |