简体   繁体   中英

Why does scanf returns control back to the program on pressing Enter key?

I wrote the following program.

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

I input "3" followed by a TAB from the keyboard. Nothing happens. Then, I press Enter Key. I get "3" printed and the program ends.

If "TAB" [Horizanotal Tab] and "Enter" [Newline] are both whitespace characters, why is their behaviour different?

Details are operating system specific (since standard C99 does not know about terminals).

I assume you are using Linux .

First, stdio(3) is buffering the standard input stream and most other FILE* streams. You might try to change that with setvbuf(3) , but this affects output buffering only.

More importantly, when stdin (actually the file descriptor used by it, ie STDIN_FILENO which is generally the value of fileno(stdin) ) is a terminal (see isatty(3) to test that), the linux kernel is usually line-buffering the terminal (so called cooked mode ) - at least to handle the backspace key. You might change that by switching the tty to raw mode (as every editor like emacs or vim or nano would do). See this question . But you should reset the cooked mode before your program exits.

So in normal cases, two levels of buffering happen: in the kernel for the line discipline of the terminal, and in the libc for the buffering of stdin

Read the tty demystified page and the Text Terminal HowTo

In practice, if you want sophisticated terminal input, use some library like ncurses or readline (don't bother using just termios)

See also stty(1) & termios(3) & tty_ioctl(4) ; read about ANSI escape codes .

Notice that this line buferring at two levels ( libc and kernel) is specific to ttys. When stdin is a pipe(7) (as in echo foo | yourprogram ) or a file (as in yourprogram < yourinputfile.txt ) things are different.

In short, ttys are hard to understand, because they mimic complex and arcane hardware devices of the 1950s-1970s era.

Most OSes buffer keyboard input so that they can process backspaces properly -- the OS keeps input in a buffer and only gives it to the program when Enter is hit.

Most OSes also provide ways to control this, but the way is different for different OSes. On POSIX systems, the tcsetattr command is used to control this terminal buffering, along with lots of other things. You can read the termios(3) manual page for lots of information about it. You get the behavior you want by setting non-canonical mode:

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

which causes the OS to send every keystroke to your program immediately (except for a few special ones it intercepts, like ctrl-C ), without waiting for Enter , and without processing backspaces either.

Note that terminal settings are persistent across programs that use the same terminal, so you probably want to save the original settings as of when your program started and restore them before it exits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM