带有 SEEK_CUR 的 fseek() 函数中的零偏移有什么用?

What is the use of zero offset in fseek() function with SEEK_CUR?

提问人:osmangokalp 提问时间:5/14/2023 最后编辑:Solomon Uckoosmangokalp 更新时间:5/16/2023 访问量:2394

问:

while (fread(&product, sizeof(Product), 1, file) == 1) {
        product.price *= 2.0;
        fseek(file, -sizeof(Product), SEEK_CUR);
        fwrite(&product, sizeof(Product), 1, file);
        fseek(file, 0, SEEK_CUR);
}

使用上面的代码,我试图将二进制文件中每个产品的价格翻倍,其中每条记录都是 Product 结构的一个实例。当我运行此代码时,它正确更新了文件中的所有价格。

据我所知,自动将文件指针移动到下一条记录。使用 first ,代码将文件指针移回记录的开头。然后它使用 更新它。 自动将文件指针移动到下一条记录。在 while 循环中,应继续读取下一条记录,依此类推。freadfseekfwritefwritefread

这里似乎没有必要。但是,如果我删除它,代码会进入无限循环。我不明白为什么会这样。fseek(file, 0, SEEK_CUR);

我试图使用 .但是我没有看到它的价值有任何变化。ftellfseek(file, 0, SEEK_CUR);

C File IO 语言律师 Fseek

评论

15赞 chux - Reinstate Monica 5/14/2023
注意:更好的代码会检查 和 的返回值fwrite()fseek().

答:

67赞 Andreas Wenzel 5/14/2023 #1

当打开流进行读取和写入时,不允许直接在读取和写入之间切换。ISO C11 标准的 §7.21.5.3 ¶7 规定如下:

当使用更新模式打开文件时(“+”作为上述模式参数值列表中的第二个或第三个字符),可以对关联的流执行输入和输出。但是,如果不干预调用 fflush 函数或文件定位函数(fseek、fsetpos 或 rewind),则输出不应直接跟在输入后面,除非输入操作遇到文件末尾。

如果删除该行

fseek(file, 0, SEEK_CUR);

然后,您的程序违反了此规则,这将调用未定义的行为。这意味着任何事情都可能发生,包括您的程序陷入无限循环的可能性。

评论

9赞 chqrlie 5/15/2023
好答案。您可能还会提到,如果文件未以二进制模式打开,则发布的代码没有定义的行为。
1赞 Andreas Wenzel 5/15/2023
@chqrlie:由于 OP 声明该文件是二进制的,因此我假设该文件要么是以二进制模式打开的,要么是 OP 使用的平台在文本模式和二进制模式之间没有区别(例如 POSIX/Linux)。但是,您是正确的,根据 ISO C11 标准的 §7.21.9.2 ¶4,该行将在文本模式下调用未定义的行为。但是,在 POSIX 平台(如 Linux)上,行为是定义的。fseek(file, -sizeof(Product), SEEK_CUR);
0赞 Joshua 5/16/2023
好奇能不能做。fflush()
0赞 Joshua 5/16/2023
@chqrlie:除非他处于二进制模式,或者他在 .Productchar[]fwrite()
0赞 chqrlie 5/16/2023
@Joshua:如答复所述,可以替换调用,并且刷新可能更可靠,但也更昂贵。fseek(file, 0, SEEK_CUR);fflush(file);
7赞 supercat 5/16/2023 #2

一些执行随机寻道速度较慢的文件系统旨在通过使用单独的读写指针有效地处理对一系列记录的读-修改-写操作。像您这样的程序可能能够完全省略这些操作,前提是读取的数据量与写入的数据量相匹配,并且如果没有这些操作,它的运行速度可能比包含这些操作时运行得更快。fseekfseek()

C 标准库实现需要跟踪对文件执行的最后一个操作是写入还是读取,以便相对可以相对于当前读取位置或当前写入位置进行操作,具体取决于上次执行的操作类型,但可以允许编写以利用双文件指针的程序在面向使用它们的系统时获得相关的性能提升。fseek()

尽管该标准可以将不进行干预的写入和读取行为归类为实现定义,但这将被视为意味着实现应尝试以一致的记录方式运行,即使针对具有并不总是可预测的极端情况行为的平台。fseek()

评论

1赞 grahamj42 5/16/2023
我本来要写这个答案的!当省略第二个 fseek() 时,OP 报告的循环行为与他的运行时具有单独的读写指针一致,这些指针由 fseek() 同步。
1赞 supercat 5/17/2023
@grahamj42:需要注意的是,Standrad 旨在为程序员提供编写可移植程序的“战斗机会”,而不是确保程序员能够使用可移植代码实现与使用非可移植结构相同的性能水平。如果系统设计为在没有 fseek 的情况下最大限度地提高读-修改-写入模式的性能,并且不是为了优化 fseek 性能而设计的,则每条记录使用两个 fseek 可能会使性能降低一个数量级。