我尝试分析以下代码:
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会很有帮助我在这里看到了相关的问题,但没有找到任何特别解决我的疑问。
1条答案
按热度按时间p1tboqfb1#
这是未定义的行为。
在 * 某些 * 系统上(即
libc
变体),三个标准流定义在数组中(例如):这就是为什么它 * 似乎 * 工作。因为有了:
out
是stdin
。但是,这只是 * 一 *
libc
。它可以与其他系统完全不同。* 没有 * 要求[或 * 保证 *]FILE
描述符位于连续内存中。如果您有(例如):
那么,“诡计”就不会起作用。
别用这个。
char ch;
更改为int ch;
。否则,如果您在输入上获得了合法的0xFF,它将被错误地解释为-1(即EOF
)。更新:
在一个标准流定义在数组中的系统上,如第一个示例所示,OP代码的行为定义得非常好,应该可以可靠地工作,因此不合格的“It's undefined behavior”是不正确的。然而,它没有指明是否定义了行为,并且依赖于该行为的程序并不严格符合语言规范。- -约翰·博林格
所以,如果我说“未指定”,事情就没问题了?如何可靠地检测到这一点?
即使在
glibc
上[我检查过了],除非我们做#ifdef __GLIBC__
或类似的东西,否则人们怎么知道呢?否则,我认为我们必须假设
stdout
指向一个 * 单个 * 示例。如果我们这样做:[没有咨询规范]我认为这是UB。
我们可以很容易地拥有:
并且,数组元素从(例如)
malloc
+一些设置初始化。再说一次,这行不通。
更新#2:
如果我理解错了请纠正我。所以,当我写out=out-1时,它进入stdin并等待用户输入。
对于术语,我不确定我会说“goes into
stdin
”。我会说“points tostdin
”或“is set to the value ofstdin
”。在我给予输入之后,它从相同的stdin输入中读取并逐个打印出字符。对吗?- 神经元B
在这一点上,使用“技巧”[你不能使用],我们让
out
指向与stdin
相同的地址。所以,要做到这一点的“干净”方法是:在此之后,这只是一个简单的 * 循环 *:
1.从
out
流中读取一个字符[即 nowstdin
]1.如果字符读取是
EOF
,则循环结束。1.将其打印到显示器(即,
stdout
)。