2011-04-13 16 views
4

作为一个小背景,我对C编程语言颇为陌生,因此一直试图通过第二版的Kernighan & Ritchie手册中的一些练习。我意识到我可以通过更多地使用标准库来更加简洁地处理某些问题,但是我尽可能地尽量保持我的有用命令的剧目与本书同步。为什么我需要多个EOF(CTRL + Z)字符?

如果它有所作为,我使用Tiny C编译器(TCC)在Windows XP环境下编译源代码,并在XP控制台(cmd.exe)内执行二进制文件。

问题:处理End-of-File (EOF) characters。我已经整理了一个小测试用例来说明问题。该程序似乎处理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; 
} 

样品输入1:abcd^Z[enter](其中^ Z/CTRL + Z表示EOF字符和[输入]表示输入键。)

示例输出1:Count: 4(等待更多的输入或端部上正确^ C/^ Z [输入])

样品输入2:abcd^Zefgh

示例输出2:Count: 4(对于更多的输入等待或端上正确^ C/^ Z [输入])

正如在这两个例子中所指出的,直到^ C/^ Z [输入]序列被启动后才会输出字符数。在开始之前,程序等待(并确实处理)更多输入。但是,如例2所述,当程序遇到最初的^ Z时,它会停止处理该输入行,等待更多输入或在启动^ C/^ Z [输入]序列时返回正确的计数。

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

+0

作为一个更新,我确定Tiny C编译器似乎确认了EOF字符^ Z。^Z(输入)和EOF(符号常量)都被识别为值-1。 – bfisher 2011-04-13 22:23:13

+0

另一个更新,我定义了另一个符号常量EOT(End-of-Transmission)为4.现在,当我测试这个常数时,通过发出一个^ D(被认为是值4),所有工作都按照计划进行。但是,如果将EOT定义为-1,则EOF将再次破坏程序。这看起来很奇怪,因为^ Z被程序识别为值-1。 – bfisher 2011-04-14 00:25:31

+0

你错误地将'^ Z'看作一个角色。无论终端/控制台级别的哪个字符被指定为生成EOF,都会在另一个层到达您的应用程序之前进行解释。在Windows上,'^ D'默认情况下并不是特殊的,所以你只需要获得字节'^ D'(4)。 – 2011-04-15 11:27:54

回答

6

这个答案是unix-ish,但我认为类似的phenonemon正在Windows上发生。 EOF的基本形式是零长度read。在交互式输入设备(终端)上,在输入流中有一个特殊的EOF机制,但是如果已经有输入被读取,它将与该输入一起被消耗(导致非零长度read),因此从来没有注意到应用程序。只有当EOF发生而没有事先输入缓冲时,它才会被应用程序注意到并采取行动。

如果您有权访问Linux(或其他* nix)系统,请编写类似的测试程序并在strace下运行它。注意发生的基本read调用,而这种非直觉行为的原因是有道理的。

+0

该信息非常感谢。我目前没有一台* nix机器在运行,但为了测试这个假设的唯一目的,必须在接下来的几天内设置一台* nix机器。然而,如果EOF信号按照建议被消耗,我想知道为什么程序不会继续愉快地阅读剩下的任何内容(除非在程序获得那么多之前,或许cmd在^ Z之后被截断)。 – bfisher 2011-04-14 00:31:30

+0

这可能是我不熟悉的Windows的一个特点。 :-) – 2011-04-14 01:19:01

0

我不确定TCC,但在相当多的情况下(大多数情况下),您需要输入^ Z本身或多或少才能被识别为EOF(即,您需要一个[enter]^z [enter])的顺序)。

0

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

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

+0

是的,^ C/CTRL + C发送一个SIGINT信号或导致当前正在运行的进程退出的东西(这就是为什么它工作不管)。但是,我仍然不明白为什么abcd^Zefgh只在EOF字符没有被处理时才计算abcd。如果是这样,为什么程序继续循环并在其后收集输入。 – bfisher 2011-04-13 20:55:20

+0

@bfisher,我认为现在接受这个答案为时过早 - 我没有完全回答这个问题,是吗? – 2011-04-13 21:50:36

1

这可以追溯到计算的石器时代。至少CP/M,早在DEC早期的操作系统可能会更长。 CP/M没有存储文件的大小,它只跟踪磁盘扇区的数量,每个磁盘扇区为128个字节。对于二进制文件来说不是一个问题,一个程序只要有足够的时间就停止阅读。但肯定是文本文件的问题。

因此,按照惯例,文本文件的文件结尾标有代码0x1a,Control + Z。由于文本文件的遗留问题比文本文件数量大,因此必须在每一代CRT实现中继续使用。 Windows并没有提供关于它的窍门,这纯粹是一个CRT实现细节。这就是为什么在控制台上键入Ctrl + Z并不会做任何特殊处理的原因。一旦按Enter键,cmd.exe中的CRT再次调用传统行为并声明EOF。

0

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