知识铺垫:EOF

EOF 就是个 int 宏常量,表示 end-of-file,文档中有下面这么一段话:

image-20220509120708383

阮一峰老师也讲过:EOF是什么?

1
2
3
char ch;
ch = getchar(); // 直接 ^D,就能看到 EOF 值为 -1
printf("%d\n", ch);

image-20220509123350672

本图可点击

单字符的I/O

假设我想读入单个字符,该怎么办呢 ?=> 用getchar()

假设我想输出单个字符,该怎么办呢 ?=> 用putchar()

当然你可以可用 scanf() 和 printf(),并使用 %c

1
2
3
char ch;
scanf("%c", &ch);
printf("%c", ch);

关于 getchar() 的返回值是 int 这件事

文档中有下面这么一段话:

image-20220509120135429

举个例子

1
2
3
4
5
char ch;
while ((ch = getchar()) != EOF) {
putchar(ch);
}
printf("EOF\n");

这段代码的执行结果十分神奇

在命令行中,编译执行这段程序,命令行就会等待你用键盘输入内容,这符合预期

但接下来发生的事情可能就会和你的想法不太一样了

首先,你可以连续敲击键盘进行输入,只有当你按下「回车」,命令行才会有所回应

它会将你输入的内容原封不动地输出到你的显示屏上(就像 echo 一样)

接下来,你的命令行会继续等待你用键盘输入内容,并会如此循环往复

如果想停掉这个程序,你只能 ^C(你输入 EOF 或 -1 都不行),但并不会输出代码最后一行的 EOF

image-20220509124131967

这说明我们并没有正确地输入 EOF,也就并没有让我们的程序知道「输入结束」这件事情,我们只是在程序运行的中途就直接强制退出了该程序

那如何向 Shell 输入 EOF ?

Unix(Linux):^D

DOS(Windows):^Z

image-20211120151345087

image-20211120151456668

要理解上面这段程序背后发生了什么,我们必须了解一些关于 Shell 和其中的缓冲区的知识

Shell

键盘/显示器 <= Shell => 应用程序

Shell 是一个特殊的程序,它能将你的程序 run 起来,也能将运行着的程序 kill 掉

当 Shell 监听到用户按下了 ^C,运行着的程序就被 kill 掉了…

Shell 中的缓冲区

当用户在命令行中编译执行上面那段程序时,Shell 会负责将其 run 起来

不难看出程序会停在 getchar(),等待用户敲击键盘进行输入

比如你输入了一个字符 a,其实这个字符并没有被立刻送入你的应用程序,而是先被送到了 Shell 那里

Shell 会将收到的字符放在自己的缓冲区中,'\0'是缓冲区内容末尾的标志,就像下图这样

image-20211120153955045

由于此时 Shell 并没有接收到(回车符 || EOF),所以 Shell 会继续等待用户的输入(应用程序此时并不读取 Shell 的缓冲区中的内容)

假设你又输入了两个字符 b 和 c,缓冲区就变成了下面这个样子

image-20211120155135846

一旦你按下了「回车」,你的应用程序就会开始从 Shell 的缓冲区中读取内容(刚按下回车的缓冲区就像下面这样)

image-20211120155714429

上面那段程序中,getchar() 逐个字符地读取,读一个字符就输出一个字符

一旦读到 \0 就说明读到了缓冲区的末尾,对于上面那段程序,由于此时还没有读到 EOF,所以 while 循环会继续,也就会继续停在 getchar() 等待用户的输入

当用户按下了 ^D,Shell 的缓冲区就会收到信号 EOF,此时你的应用程序立刻就会读到 EOF

对于上面那段应用程序,不难看出就跳出了 while 循环,继续向下执行,直到程序全部执行结束,正常退出。

另外:缓冲区也可以从文件中读取内容

(完)