为什么scanf在按Enter键时将控制权返回给程序?

时间:2015-03-15 06:26:02

标签: c scanf

我写了以下程序。

void main()
{
   int   *piarrNumber1   = (int *) calloc(1, sizeof(int));
   int   iUserInput      = 0;

   scanf("%d", &iUserInput);
   piarrNumber1[(sizeof piarrNumber1 / sizeof(int)) - 1] = iUserInput;
   printf("\n%d\n", piarrNumber1[0]);
}

我从键盘输入“3”后跟一个TAB。什么都没发生。然后,我按Enter键。我打印“3”,程序结束。

如果“TAB”[Horizanotal Tab]和“Enter”[Newline]都是空白字符,为什么他们的行为不同?

2 个答案:

答案 0 :(得分:4)

详细信息是特定于操作系统的(因为标准C99不了解终端)。

我假设您使用的是 Linux

首先,stdio(3)正在缓冲标准输入流和大多数其他FILE*流。您可能会尝试使用setvbuf(3)更改它,但这只会影响输出缓冲。

更重要的是,当 stdin (实际上它使用的文件描述符,即STDIN_FILENO,通常是fileno(stdin)的值)是一个终端(参见{{3测试那个),linux内核通常对终端进行行缓冲(所谓的isatty(3)) - 至少要处理 backspace 键。您可以通过将tty切换为原始模式来更改它(因为emacsvimnano等每个编辑器都会这样做。见cooked mode。但是你应该在程序退出之前重置熟化模式。

因此,在正常情况下,会发生两个级别的缓冲:在内核中用于终端的线路规则,在libc中用于缓冲stdin

阅读this question页面和tty demystified

在实践中,如果您需要复杂的终端输入,请使用某些库,例如Text Terminal HowToncurses (不要只使用termios)

另见readline& stty(1)& termios(3);阅读tty_ioctl(4)

请注意,此行缓冲在两个级别(libc和内核)特定于ttys。当stdinANSI escape codes(如echo foo | yourprogram)或文件(如yourprogram < yourinputfile.txt)时,情况会有所不同。

简而言之,ttys很难理解,因为它们模仿了20世纪50年代到70年代的复杂而神秘的硬件设备。

答案 1 :(得分:3)

大多数操作系统缓冲键盘输入,以便它们可以正确处理退格键 - 操作系统将输入保存在缓冲区中,只有在 Enter 命中时才将其输入程序。

大多数操作系统也提供了控制方法,但不同操作系统的方式不同。在POSIX系统上,tcsetattr命令用于控制此终端缓冲以及许多其他事情。您可以阅读termios(3)手册页,了解有关它的大量信息。通过设置非规范模式,您可以获得所需的行为:

#include <termios.h>
#include <unistd.h>
    :
struct termios attr;
tcgetattr(0, &attr);
attr.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &attr);

导致操作系统立即向你的程序发送每个击键(除了一些特殊的击键,例如 ctrl-C ),而不等待 Enter ,并且不处理退格。

请注意,终端设置在使用相同终端的程序中是持久的,因此您可能希望保存程序启动时的原始设置,并在退出之前将其恢复。