You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fgets()是否会记录从FILE*流停止读取的位置?附代码读写疑问

核心结论

fgets()返回的指针仅代表你传给它的用户缓冲区首地址,和它内部维护的文件读取位置状态完全无关——记忆读取位置的逻辑根本不依赖这个返回值,也不依赖你传入的缓冲区内容。

位置状态的实际存储逻辑

fgets()对读取位置的追踪完全依托FILE流对象本身的内部结构,这部分状态和你代码里定义的char buff[1024]没有任何关联:

  • 内核层面:每个打开的文件对应的文件描述符,会由操作系统自动维护当前文件偏移量。所有通过系统调用发起的读操作,内核都会自动根据实际读取的字节数,把这个偏移量向后推进,下次读操作自动从新偏移位置开始,不需要应用层手动传入位置参数。
  • C标准库层面:每个FILE流对象(比如代码里的stdin)内部还会维护一层用户态的读取缓冲区,以及对应缓冲区的读取游标。这层缓冲的存在是为了减少频繁陷入内核的开销:fgets()不会每次调用都直接触发系统调用读文件,而是先从自己的内部缓冲区里拷贝数据到你传入的buff里,等内部缓冲的剩余数据不够了,才会通过系统调用一次性从内核读一大块数据到内部缓冲。
    • 每次fgets()被调用时,只会从内部缓冲区的当前游标位置开始,向你传入的buff里拷贝最多size-1个字符,遇到换行符或者EOF就提前停止,最后补字符串结束符'\0',再把内部缓冲区的游标向前推进对应拷贝的字节数。
    • 等下次fgets()被调用时,会直接接着上次的内部缓冲游标位置、以及内核已经推进的文件偏移位置继续读,全程不会读取你传入的buff里之前存过的任何内容,更不会因为你把buff里的内容通过write()写完了就重置位置。
对应你提到的场景解释

你代码里的流程完全符合上述逻辑:

  1. 第一次循环调用fgets()时,库自动从stdin对应的流内部状态取数,往buff里填最多1023字节的内容,返回buff首地址。
  2. 你通过do-while循环把buff里的内容全部write()到stdout的过程,完全不会改动stdin对应的流内部状态。
  3. 第二次循环调用fgets()时,它会直接跳过buff里上次残留的旧内容,根据自己内部维护的游标、内核维护的文件偏移位置,把新的后续读取到的内容直接覆盖写进buff,最后还是返回同一个buff的首地址。

你之前产生困惑的核心原因,是误把“返回同一个缓冲区地址”当成了“fgets没有位置记忆能力”,实际上记忆位置的模块和fgets的返回值、以及你传入的用户缓冲区是完全解耦的。

内容的提问来源于stack exchange,提问作者tonythestark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 09:15:04