为什么在 stdin 上使用 ftell() 会导致非法的 seek 错误?

9
以下代码输出"Illegal seek":
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    errno = 0;
    getchar();
    getchar();
    getchar();
    ftell( stdin );
    printf( "%s\n", strerror(errno) );
}

当我运行cat script | ./a.out 或者只是运行./a.out时,就会出现这个问题。当然,问题出在ftell上。我的问题是:为什么会发生这种情况?我认为stdin是可定位的。fseek也会导致相同的错误。如果stdin不可定位,那么有没有办法做同样的事情呢?
谢谢您的回复。

1
你实际上想要用fseek解决什么问题? - user25148
请将指向stdin的文件指针与fseek一起使用。相关信息请参考:https://dev59.com/Qm445IYBdhLWcg3wUoxe - Ciro Santilli OurBigBook.com
1个回答

15

管道是不可寻址的。它们只是一个缓冲区。一旦数据从管道缓冲区中被 read() 读取,就不能再次检索。

请注意,如果您运行了您的程序:

./a.out < script

那么标准输入会是一个文件而不是一个管道,因此ftell()将按照你的期望工作。


2
请提供一个示例,展示cat file|./a.out./a.out<file之间的区别。 - R.. GitHub STOP HELPING ICE
@R.. 有什么区别? - Calmarius
@Calmarius:cat f|x使用单独的进程(可能是单独的CPU)将文件复制到FIFO中。 FIFO由进程x读取。如果x除了read()之外还做其他事情,那么这可能比它自己读取文件要快。 x<f在同一进程中打开f,如果x除了读取文件之外没有做太多事情,那么这可能更快,并实际上给您提供了可寻址的文件(因为FIFO只是一个缓冲区)。 - geocar
值得一提的是,./a.out < script 很可能是由 Bash 使用 freopen 实现的。 - Ciro Santilli OurBigBook.com
嗨@geocar,我想知道你在这里说“缓冲区”是指什么?“Fifos不可寻址。它们只是一个缓冲区。”,你是指它只是一个驻留在内存中的char数组吗? - Rick
1
不好意思,Rick,我是说什么就是什么:重要的属性是它不可寻址,也就是说,你不能使用lseek()、fseek()或任何其他寻址方式来寻址它,因为这些方法都行不通。内核如何在内存中排列该缓冲区与“为什么此代码不起作用”无关,考虑这个问题只会导致愚蠢的想法。 - geocar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接