从标准输出使用文件指针读取并递减它。

huangapple go评论59阅读模式
英文:

Reading from stdout using file pointer and decrementing it

问题

The code you provided is written in C and seems to be reading characters from the standard input (stdin) and printing them one by one until it reaches the end of the file (EOF). Let's address your questions:

  1. out = out - 1; is decrementing the file pointer out by 1, which means it's moving one character position backward in the file. Since out initially points to stdout, this operation is shifting the file pointer backward in the standard output stream. It doesn't affect the standard input stream (where user input is expected).

  2. The code expects user input because of ch = fgetc(out);. It reads characters from the file stream pointed to by out, which, after the decrement operation, is still connected to stdout. So, it doesn't read from the standard input; instead, it reads from the standard output, which may not be what was intended.

In summary, the code is a bit unconventional and may not behave as expected. If the goal is to read user input, you should use ch = fgetc(stdin); to read from the standard input stream, and you don't need the out = out - 1; line.

If you have further questions or need clarification, please feel free to ask.

英文:

I am trying to analyze the following piece of code:

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);

The following output is observed when I run this code:

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:


I understand that fgetc is fetching the value from out(which is pointing to stdout) one character at a time and printing it till it reaches the file end.

What is out=out-1 doing here? If out was originally pointing to the beginning of the stdout file, then shouldn't it be printing chars from "Enter value:" as well. Or if it is pointing to the end of the stdout file, then shouldn't it be printing only one character from the end? Also, why is it expecting user input in the first place? Is it because of getc?

Sorry for the many questions. If someone clarifies those doubts, it will be very helpful for me to understand C. I have seen related questions here but haven't found any particularly addressing my queries.

答案1

得分: 5

这是未定义行为。

在某些系统上(即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 代码的行为是完全定义的,并且应该可以可靠地工作,因此对“它是未定义行为”的不加限定的说法是不正确的。然而,行为是否被定义是未指定的,并且依赖于该行为的程序不符合语言规范。 –
约翰·博林格

那么,如果我说“未指定”,事情就会好吗?有什么方法可以可靠地检测这一点呢?

即使在 glibc 上[我检查过的地方],除非我们进行 #ifdef __GLIBC__ 或类似操作,否则我们怎么知道呢。

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

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

[不查阅规范] 我认为这是未定义行为。

我们也可能有:

FILE *__stdlist[3];

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

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

同样,它不会起作用。


更新 #2:

请纠正我是否错误地理解了您的回答。所以,当我写 out = out - 1 时,它进入 stdin 并等待用户输入。

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

在我提供输入后,它从相同的 stdin 输入读取并逐个字符打印出来。这是正确的吗? –
神经元B

在那一点上,使用“技巧” [你不能使用],我们有 out 指向与 stdin 指向相同的地址。因此,用一个_干净_的方式做到这一点是:

FILE *out = stdin;

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

  1. out 流中读取一个字符[现在是 stdin]
  2. 如果读取的字符是 EOF,循环结束。
  3. 将其打印到显示器上(即 stdout)。
英文:

It's undefined behavior.

On some systems (i.e. libc variant), the three standard streams are defined in an array (e.g):

FILE __stdlist[3];

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

That's why it seems to work. Because with:

FILE *out = stdout;

out = out - 1;

Now, out is stdin.

But, this is just one libc. It can totally different with other systems. There is no requirement [or guarantee] that the FILE descriptors are in contiguous memory.

If you had (e.g.):

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

Then, the "trick" would not work.

Do not use this.


Side note: Change char ch; into int ch;. Otherwise, if you got a legitimate 0xFF on the input, it would erroneously be interpreted as -1 (i.e. EOF).


UPDATE:
>On a system where the standard streams are defined in an array, as shown in your first example, the behavior of the OP's code is perfectly well defined, and should be expected to work reliably, so an unqualified "It's undefined behavior" is incorrect. However, it is unspecified whether the behavior is defined, and a program that relies on that behavior does not conform strictly to the language spec. –
John Bollinger

So, if I said unspecified, things would be okay? How would one reliably detect this?

Even on glibc [which does this--I checked], how does one know unless we do #ifdef __GLIBC__ or some such.

Otherwise, I think we must assume stdout points to a single instance. If we do:

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

[Without consulting the spec] I think that's UB.

We could just as easily have:

FILE *__stdlist[3];

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

And, the array elements are initialized from (e.g.) malloc + some setup.

And, again it won't work.


UPDATE #2:
>please correct me if I am wrong in understanding your answer. So, when I write out=out-1, it goes into stdin and waits for user input.

For terminology, I'm not sure I would say "goes into stdin". I'd say it "points to stdin" or "is set to the value of stdin".

>After I give the input, it reads from the same stdin input and prints out the characters one by one. Is that correct? –
NeuronB

At that point, using the "trick" [which, you can't use], we have out pointing to the same address as stdin points to. So, the clean way to do this is:

FILE *out = stdin;

After that, this is just a simple loop that:

  1. reads one char from the out stream [which is now stdin]
  2. if the char read is EOF, the loop is ended.
  3. prints it to the display (i.e. stdout).

huangapple
  • 本文由 发表于 2023年5月11日 01:13:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76221033.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定