C语言 使用文件指针从stdout阅读并递减它

6za6bjd0  于 2023-05-16  发布在  其他
关注(0)|答案(1)|浏览(102)

我尝试分析以下代码:

FILE* out=stdout;
    
    printf("\nEnter value: \n");
    
    out=out-1;
    
    char ch;
    
    while(ch!=EOF){
        ch=fgetc(out);
        printf("\nValue printed: %c\n",ch);
    }
    
    fclose(out);

当我运行这段代码时,观察到以下输出:

Enter value:
IWantToC

Value printed: I

Value printed: W

Value printed: a

Value printed: n

Value printed: t

Value printed: T

Value printed: o

Value printed: C

Value printed:

我知道fgec是从out(指向stdout)中一次获取一个字符的值,并打印它,直到它到达文件末尾。
out=out-1在这里做什么?如果out最初指向stdout文件的开头,那么它不应该也打印来自“Enter value:”的字符吗?或者,如果它指向stdout文件的末尾,那么它不应该只打印从末尾开始的一个字符吗?另外,为什么它首先期望用户输入?是因为gec吗?
抱歉问了这么多问题。如果有人澄清那些疑惑,对我理解C会很有帮助我在这里看到了相关的问题,但没有找到任何特别解决我的疑问。

p1tboqfb

p1tboqfb1#

这是未定义的行为。
在 * 某些 * 系统上(即libc变体),三个标准流定义在数组中(例如):

FILE __stdlist[3];

#define stdin  (&__stdlist[0])
#define stdout (&__stdlist[1])
#define stderr (&__stdlist[2])

这就是为什么它 * 似乎 * 工作。因为有了:

FILE *out = stdout;

out = out - 1;

outstdin
但是,这只是 * 一 * libc。它可以与其他系统完全不同。* 没有 * 要求[或 * 保证 *] FILE描述符位于连续内存中。
如果您有(例如):

FILE *stderr;
int x;
FILE *stdout;
int y;
FILE *stdin;
int z;

那么,“诡计”就不会起作用。
别用这个。

  • 边注:* 将char ch;更改为int ch;。否则,如果您在输入上获得了合法的0xFF,它将被错误地解释为-1(即EOF)。
    更新:

在一个标准流定义在数组中的系统上,如第一个示例所示,OP代码的行为定义得非常好,应该可以可靠地工作,因此不合格的“It's undefined behavior”是不正确的。然而,它没有指明是否定义了行为,并且依赖于该行为的程序并不严格符合语言规范。- -约翰·博林格
所以,如果我说“未指定”,事情就没问题了?如何可靠地检测到这一点?
即使在glibc上[我检查过了],除非我们做#ifdef __GLIBC__或类似的东西,否则人们怎么知道呢?
否则,我认为我们必须假设stdout指向一个 * 单个 * 示例。如果我们这样做:

int val;
int *ptr = &val;
ptr = ptr - 1;

[没有咨询规范]我认为这是UB。
我们可以很容易地拥有:

FILE *__stdlist[3];

#define stdin  (__stdlist[0])
#define stdout (__stdlist[1])
#define stderr (__stdlist[2])

并且,数组元素从(例如)malloc+一些设置初始化。
再说一次,这行不通。

更新#2:

如果我理解错了请纠正我。所以,当我写out=out-1时,它进入stdin并等待用户输入。
对于术语,我不确定我会说“goes into stdin”。我会说“points to stdin”或“is set to the value of stdin”。
在我给予输入之后,它从相同的stdin输入中读取并逐个打印出字符。对吗?- 神经元B
在这一点上,使用“技巧”[你不能使用],我们让out指向与stdin相同的地址。所以,要做到这一点的“干净”方法是:

FILE *out = stdin;

在此之后,这只是一个简单的 * 循环 *:
1.从out流中读取一个字符[即 nowstdin]
1.如果字符读取是EOF,则循环结束。
1.将其打印到显示器(即,stdout)。

相关问题