使用文件指针从 stdout 读取并递减它

Neu*_*onB -1 c pointers stdout

我正在尝试分析以下代码:

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);
Run Code Online (Sandbox Code Playgroud)

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

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:


Run Code Online (Sandbox Code Playgroud)

我知道 fgetc 一次从 out(指向 stdout)获取一个字符的值并打印它直到到达文件末尾。

out=out-1 在这里做什么?如果 out 最初指向 stdout 文件的开头,那么它不应该也打印“Enter value:”中的字符。或者,如果它指向 stdout 文件的末尾,那么它不应该只从末尾打印一个字符吗?另外,为什么它首先需要用户输入?是因为getc吗?

抱歉问了很多问题。如果有人澄清这些疑问,这对我理解 C 将会非常有帮助。我在这里看到了相关问题,但没有找到任何特别解决我的疑问的问题。

Cra*_*tey 5

这是未定义的行为。

\n

某些系统(即libc变体)上,三个标准流在数组中定义(例如):

\n
FILE __stdlist[3];\n\n#define stdin  (&__stdlist[0])\n#define stdout (&__stdlist[1])\n#define stderr (&__stdlist[2])\n
Run Code Online (Sandbox Code Playgroud)\n

这就是为什么它似乎有效。因为:

\n
FILE *out = stdout;\n\nout = out - 1;\n
Run Code Online (Sandbox Code Playgroud)\n

现在outstdin

\n

但是,这只是其中之一 libc。它可以与其他系统完全不同。不要求[或保证]描述FILE符位于连续内存中。

\n

如果你有(例如):

\n
FILE *stderr;\nint x;\nFILE *stdout;\nint y;\nFILE *stdin;\nint z;\n
Run Code Online (Sandbox Code Playgroud)\n

那么,这个“诡计”就行不通了。

\n

不要使用这个。

\n
\n

旁注:更改char ch;int ch;. 否则,如果输入上有合法的 0xFF,它将被错误地解释为 -1(即EOF)。

\n
\n

更新:

\n
\n

在标准流在数组中定义的系统上,如第一个示例所示,OP代码的行为被完美定义,并且应该可靠地工作,因此不合格的“这是未定义的行为”是不正确的。但是,未指定是否定义了该行为,并且依赖该行为的程序并不严格遵守语言规范。\xe2\x80\x93\n约翰·博林格

\n
\n

那么,如果我说unspecified,事情就可以了吗?如何可靠地检测到这一点?

\n

即使在glibc[这是做#ifdef __GLIBC__什么的——我检查过],除非我们这样做或类似的事情,否则人们怎么知道。

\n

否则,我认为我们必须假设stdout指向单个实例。如果我们这样做:

\n
int val;\nint *ptr = &val;\nptr = ptr - 1;\n
Run Code Online (Sandbox Code Playgroud)\n

[无需查阅规范]我认为那是 UB。

\n

我们可以很容易地得到:

\n
FILE *__stdlist[3];\n\n#define stdin  (__stdlist[0])\n#define stdout (__stdlist[1])\n#define stderr (__stdlist[2])\n
Run Code Online (Sandbox Code Playgroud)\n

并且,数组元素是从(例如)malloc+一些设置初始化的。

\n

而且,这又行不通。

\n
\n

更新#2:

\n
\n

如果我对您的答案的理解有误,请纠正我。因此,当我写出 out=out-1 时,它会进入 stdin 并等待用户输入。

\n
\n

对于术语,我不确定我会说“进入stdin。我会说它“指向”或“stdin设置为stdin”的值。

\n
\n

在我给出输入后,它从相同的标准输入读取并一一打印出字符。那是对的吗?\xe2\x80\x93\n神经元B

\n
\n

那时,使用“技巧”[这是你不能使用的],我们out指向了与stdin指向的地址相同的地址。因此,最干净的方法是:

\n
FILE *out = stdin;\n
Run Code Online (Sandbox Code Playgroud)\n

之后,这只是一个简单的循环

\n
    \n
  1. 从流中读取一个字符[现在out是] stdin
  2. \n
  3. 如果读取的字符为EOF,则循环结束。
  4. \n
  5. 将其打印到显示器上(即stdout)。
  6. \n
\n