为什么我需要多个EOF(CTRL+;Z)字符?

为什么我需要多个EOF(CTRL+;Z)字符?,c,windows-xp,cmd,eof,C,Windows Xp,Cmd,Eof,作为一个小背景,我对C编程语言相当陌生,因此一直在尝试完成Kernighan&Ritchie手册第二版中的一些练习。我确实意识到,通过更多地利用标准库,我可能可以更简洁地处理某些问题,但我正试图尽可能使我的有用命令库与本书保持同步 如果有区别的话,我将使用Tiny C编译器(TCC)在Windows XP环境中编译源代码,并在XP控制台(cmd.exe)中执行二进制文件 问题:处理文件结尾(EOF)字符。我准备了一个小测试用例来说明这个问题。该程序似乎可以处理EOF字符(部分)。我将尝试用示例输

作为一个小背景,我对C编程语言相当陌生,因此一直在尝试完成Kernighan&Ritchie手册第二版中的一些练习。我确实意识到,通过更多地利用标准库,我可能可以更简洁地处理某些问题,但我正试图尽可能使我的有用命令库与本书保持同步

如果有区别的话,我将使用Tiny C编译器(TCC)在Windows XP环境中编译源代码,并在XP控制台(cmd.exe)中执行二进制文件

问题:处理
文件结尾(EOF)字符
。我准备了一个小测试用例来说明这个问题。该程序似乎可以处理EOF字符(部分)。我将尝试用示例输入/输出来演示该问题

#include <stdio.h>

int main() 
{
    int character, count;

    character = 0;
    character = getchar();

    for (count = 0; character != EOF; ++count) 
    {
        character = getchar();
    }

    printf("Count: %d", count);
    return 0;
}
#包括
int main()
{
整数字符,计数;
字符=0;
character=getchar();
对于(计数=0;字符!=EOF;++count)
{
character=getchar();
}
printf(“计数:%d”,计数);
返回0;
}
示例输入1:
abcd^Z[enter]
(其中^Z/CTRL+Z表示EOF字符,[enter]表示回车键。)

示例输出1:
计数:4
(等待更多输入或在^C/^Z[enter]上正确结束)

示例输入2:
abcd^Zefgh

示例输出2:
Count:4
(等待更多输入或在^C/^Z[enter]上正确结束)

如两个示例中所述,在启动^C/^Z[enter]序列之前,不会输出字符计数。在启动之前,程序等待(实际上是处理)更多的输入。但是,如例2中所述,当程序遇到初始^Z时,它会停止处理该行输入,等待更多输入,或者在启动^C/^Z[enter]序列时返回正确的计数


我不明白为什么程序只是部分处理EOF字符。在我看来,如果它截断了示例2的结尾,那么它也应该完全脱离循环。知道为什么在识别出EOF字符后,程序不会立即打印当前计数并退出吗?

我不知道TCC是否正确,但在相当多(大多数?)的情况下,您需要自己或多或少地输入^Z才能将其识别为EOF(即,您需要一个[enter]^Z[enter]序列).

当您键入^Z时,Windows不会自动生成EOF;这只是从DOS继承下来的惯例。C编译器的运行时必须识别它并设置EOF标志,我猜Tiny C不会这样做


^另一方面,Windows命令环境可以识别C。这并不一定意味着EOF,我认为这更像是一个中止信号。

这个答案是unix ish,但我认为类似的现象正在Windows上发生。EOF的基本形式是零长度
读取
。在交互式输入设备(终端)上,有一种特殊的机制用于在输入流中具有EOF,但如果已经有要读取的输入,它将与该输入一起使用(导致非零长度
读取
),因此应用程序从未注意到。只有当EOF发生时,没有预先缓冲的输入,应用程序才能注意到它并对其采取行动


如果您可以访问Linux(或其他*nix)系统,请编写一个类似的测试程序,并在
strace
下运行它。观察发生的底层
read
调用,这种非直观行为的原因将是有意义的。

这可以追溯到计算机的石器时代。至少是CP/M,可能在12月初的操作系统中使用更长的时间。CP/M没有存储文件的大小,它只记录磁盘扇区的数量,每个扇区128字节。二进制文件没有问题,当程序有足够的内存时,它就会停止读取。但对于文本文件来说肯定是个问题


因此,按照惯例,文本文件的文件结尾标记为代码0x1a,Control+Z。由于遗留的文本文件数量大于其中的文本数量,因此必须在每一代连续的CRT实现中进行。Windows没有对此大惊小怪,这纯粹是一个CRT实现细节。这就是为什么在控制台中键入Ctrl+Z没有任何特殊作用。按Enter键后,cmd.exe中的CRT将再次调用旧行为并声明EOF。

我猜标准输入是行缓冲的(在Unix上)。DOS有一些
getch()
getche()
函数的级别低于stdio,因此它们绕过了stdio缓冲。我不知道如何在Windows上禁用输入缓冲,在Unix上是通过将终端设置为非规范模式来实现的。

Yep,^C/CTRL+C发送一个SIGINT信号或导致当前运行的进程退出的某些东西(这就是为什么它无论如何都能工作)。但是,我仍然不太明白为什么在没有处理EOF字符的情况下,Zefgh只计算abcd。如果是的话,为什么程序会在此后继续循环和收集输入。@B鱼,我认为现在接受这个答案还为时过早-我没有完全回答这个问题,是吗?作为更新,我已经确定微型C编译器似乎能够识别EOF字符^Z。^Z(输入)和EOF(符号常量)被识别为值-1。另一次更新,我将另一个符号常量EOT(传输结束)定义为4。现在,当我对这个常数进行测试时,通过发出^D(被认为是值4),所有的工作都按照计划进行。但是,如果将EOT定义为-1,则程序将再次中断。这似乎很奇怪,因为程序将^Z识别为值-1。您将
^Z
视为字符是错误的。终端/控制台级别指定用于生成EOF的字符在到达您的应用程序之前由另一层进行解释