在使用 fmemopen 打开的 FILE* 上使用 rewind()
Using rewind() on a FILE* opened with fmemopen
已通过 glibc 2.24 解决——请参阅下面的更新
这是一段 C 代码(使用 gcc 5.3.1、glibc 2.23 编译):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int main() {
const char* s1="Original content of file.\n"
"still original content and some remaining original content.\n";
const char* s2="overwriting data with new content\n";
const char* s3="appended data\n";
const size_t bufsz=strlen(s1)+1;
char buf[bufsz];
FILE *f;
int r;
f=fmemopen(buf,bufsz,"w");
assert(f!=NULL);
// setbuf(f, NULL); // variant no. 1
// setbuffer(f, buf, bufsz); // variant no. 2
r=fwrite(s1,strlen(s1),1,f);
assert(r==1);
r=fflush(f);
assert(r==0);
rewind(f);
r=fwrite(s2,strlen(s2),1,f);
assert(r==1);
r=fwrite(s3,strlen(s3),1,f);
assert(r==1);
r=fclose(f);
assert(r==0);
printf("%s",buf);
}
它符合我的预期——输出是:
overwriting data with new content
appended data
and some remaining original content.
现在,联机帮助页 fmemopen(3) 建议要么禁用缓冲(取消注释变体 1),要么明确将 buf
设置为缓冲区(取消注释变体 2)。
然而,在这两种情况下,我得到的结果是:
appended data
ta with new content
ginal content and some remaining original content.
因此,附加数据并没有像预期的那样写入第二个内容,而是覆盖了第二个内容。
如果我以二进制模式打开文件(即将 "w" 模式替换为 "wb"),行为保持不变。
valgrind 不报告任何错误(除了缓冲情况下的误报"Source and destination overlap"。后者是由于尝试将 STDIO 缓冲区写入内存,这确实是同一个地址。)
怎么了?我做错了吗?或者这是一个 GLIBC 错误?
更新
8月4日,新的glibc 2.24版本发布。使用 gcc 5.4.0 和 glibc 2.24,变体号。 1(无缓冲文件)工作正常。变体 2(自缓冲版本)给出了不同但仍然错误的结果。因此,我相信 R.. 声称这是联机帮助页 fmemopen(3) 中的文档错误是正确的。我会提出错误报告 ...
Now, the manpage fmemopen(3) advices to either disable buffering (uncomment variant 1), or to explicitely set buf as buffer (uncomment variant 2).
后者肯定是未定义的行为。不要这样做。我不清楚您在 Linux 手册页中找到的文本:
Alternatively, the caller can explicitly set buf as the stdio stream
buffer, at the same time informing stdio of the buffer's size, using:
setbuffer(stream, buf, size);
正在尝试表达,但这是一个文档错误,可能应该被删除。
至于无缓冲模式不工作,这可能是一个 glibc 错误。尝试尽可能减少测试用例,针对最近的 glibc 进行测试,如果仍然发生,请在 glibc bugzilla 上提交错误报告:
https://sourceware.org/bugzilla/enter_bug.cgi?product=glibc
当您想立即了解错误时,一个安全的替代方法是调用 fflush
并检查 return 值。
已通过 glibc 2.24 解决——请参阅下面的更新
这是一段 C 代码(使用 gcc 5.3.1、glibc 2.23 编译):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int main() {
const char* s1="Original content of file.\n"
"still original content and some remaining original content.\n";
const char* s2="overwriting data with new content\n";
const char* s3="appended data\n";
const size_t bufsz=strlen(s1)+1;
char buf[bufsz];
FILE *f;
int r;
f=fmemopen(buf,bufsz,"w");
assert(f!=NULL);
// setbuf(f, NULL); // variant no. 1
// setbuffer(f, buf, bufsz); // variant no. 2
r=fwrite(s1,strlen(s1),1,f);
assert(r==1);
r=fflush(f);
assert(r==0);
rewind(f);
r=fwrite(s2,strlen(s2),1,f);
assert(r==1);
r=fwrite(s3,strlen(s3),1,f);
assert(r==1);
r=fclose(f);
assert(r==0);
printf("%s",buf);
}
它符合我的预期——输出是:
overwriting data with new content appended data and some remaining original content.
现在,联机帮助页 fmemopen(3) 建议要么禁用缓冲(取消注释变体 1),要么明确将
buf
设置为缓冲区(取消注释变体 2)。然而,在这两种情况下,我得到的结果是:
appended data ta with new content ginal content and some remaining original content.
因此,附加数据并没有像预期的那样写入第二个内容,而是覆盖了第二个内容。
如果我以二进制模式打开文件(即将 "w" 模式替换为 "wb"),行为保持不变。
valgrind 不报告任何错误(除了缓冲情况下的误报"Source and destination overlap"。后者是由于尝试将 STDIO 缓冲区写入内存,这确实是同一个地址。)
怎么了?我做错了吗?或者这是一个 GLIBC 错误?
更新
8月4日,新的glibc 2.24版本发布。使用 gcc 5.4.0 和 glibc 2.24,变体号。 1(无缓冲文件)工作正常。变体 2(自缓冲版本)给出了不同但仍然错误的结果。因此,我相信 R.. 声称这是联机帮助页 fmemopen(3) 中的文档错误是正确的。我会提出错误报告 ...
Now, the manpage fmemopen(3) advices to either disable buffering (uncomment variant 1), or to explicitely set buf as buffer (uncomment variant 2).
后者肯定是未定义的行为。不要这样做。我不清楚您在 Linux 手册页中找到的文本:
Alternatively, the caller can explicitly set buf as the stdio stream buffer, at the same time informing stdio of the buffer's size, using:
setbuffer(stream, buf, size);
正在尝试表达,但这是一个文档错误,可能应该被删除。
至于无缓冲模式不工作,这可能是一个 glibc 错误。尝试尽可能减少测试用例,针对最近的 glibc 进行测试,如果仍然发生,请在 glibc bugzilla 上提交错误报告:
https://sourceware.org/bugzilla/enter_bug.cgi?product=glibc
当您想立即了解错误时,一个安全的替代方法是调用 fflush
并检查 return 值。