C 为什么我仍然需要按';输入“;为了让输入即使在非规范模式下也能被读取和输出?

C 为什么我仍然需要按';输入“;为了让输入即使在非规范模式下也能被读取和输出?,c,terminal,buffer,ubuntu-14.04,putchar,C,Terminal,Buffer,Ubuntu 14.04,Putchar,我正在测试GNU libc手册中的代码: #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <termios.h> /* Use this variable to remember original terminal attributes. */ struct termios saved_attributes; void reset_input_mode (

我正在测试GNU libc手册中的代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>

/* Use this variable to remember original terminal attributes. */

struct termios saved_attributes;

void
reset_input_mode (void)
{
  tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}

void
set_input_mode (void)
{
  struct termios tattr;
  char *name;

  /* Make sure stdin is a terminal. */
  if (!isatty (STDIN_FILENO))
    {
      fprintf (stderr, "Not a terminal.\n");
      exit (EXIT_FAILURE);
    }

  /* Save the terminal attributes so we can restore them later. */
  tcgetattr (STDIN_FILENO, &saved_attributes);
  atexit (reset_input_mode);

  /* Set the funny terminal modes. */
  tcgetattr (STDIN_FILENO, &tattr);
  tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
  tattr.c_cc[VMIN] = 1;
  tattr.c_cc[VTIME] = 0;
  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}

int
main (void)
{
  char c;

  set_input_mode ();

  while (1)
    {
      read (STDIN_FILENO, &c, 1);
      if (c == '\004')          /* C-d */
        break;
      else
        putchar (c);
    }

  return EXIT_SUCCESS;
}
#包括
#包括
#包括
#包括
/*使用此变量可记住原始端子属性*/
结构termios保存的\u属性;
无效的
重置输入模式(无效)
{
tcsetattr(标准文件号、TCSANOW和保存的属性);
}
无效的
设置输入模式(无效)
{
结构termios tattr;
字符*名称;
/*确保stdin是一个终端*/
如果(!isatty(标准文件号))
{
fprintf(stderr,“不是终端。\n”);
退出(退出失败);
}
/*保存终端属性,以便稍后恢复它们*/
tcgetattr(标准输入文件号和保存的属性);
atexit(复位输入模式);
/*设置有趣的终端模式*/
tcgetattr(标准文件号和tattr);
tattr.c_lflag&=~(ICANON | ECHO);/*清除ICANON和ECHO*/
tattr.c_cc[VMIN]=1;
tattr.c_cc[VTIME]=0;
tcsetattr(标准文件号、TCSAFLUSH和tattr);
}
int
主(空)
{
字符c;
设置输入模式();
而(1)
{
读取(标准文件号和c,1);
如果(c=='\004')/*c-d*/
打破
其他的
普查尔(c);
}
返回退出成功;
}
即使终端被设置为非规范模式,我仍然需要按enter键才能接收输入


但是,如果我将:
putchar(c)
更改为
write(STDOUT\u FILENO,&c,sizeof(char))
,它会像我所想的那样正常工作。

用户缓冲正在战胜你<代码> PUCHAR(3)< /C>是LBC I/O API的一部分,而<>代码>写(2)是一个系统调用——嗯,不是一个系统调用,但为了简单起见,让我们考虑一下是现在。< /P> libc中有三种类型的缓冲:无缓冲、块缓冲和行缓冲。如果一个流是无缓冲的,数据一经写入就进入底层文件(或终端);如果它是块缓冲的,数据将保存在内存块中,直到它填满,然后一次写入;但是,如果它是行缓冲的,当发现换行符时,数据将传输到文件(或终端)

如果一个流连接到一个终端,就像标准输出的情况一样,它是行缓冲的。因此,这就是您的情况:当您按enter键时,换行符
\n
将导致(行)缓冲区写入标准输出。但是,当调用
write(2)
时,会绕过libc用户缓冲,并将数据写入相应的文件描述符(STDOUT\u FILENO)

因此,正如我前面所说,
write(2)
是一个系统调用;但实际上,当您调用
write
时,您是在调用系统调用的库包装器,它处理系统调用遵循的严格协议(例如,它期望参数的位置等)

顺便说一下,我在这里所说的一切都可以在
putchar(3)
write(2)
setbuf(3)
的手册页中找到。括号中的数字指的是手册中的章节:
2
用于系统调用,
3
用于库函数(
man
应提供章节及其主题的列表)


希望它能帮上忙。

用户缓冲正在战胜你<代码> PUCHAR(3)< /C>是LBC I/O API的一部分,而<>代码>写(2)是一个系统调用——嗯,不是一个系统调用,但为了简单起见,让我们考虑一下是现在。< /P> libc中有三种类型的缓冲:无缓冲、块缓冲和行缓冲。如果一个流是无缓冲的,数据一经写入就进入底层文件(或终端);如果它是块缓冲的,数据将保存在内存块中,直到它填满,然后一次写入;但是,如果它是行缓冲的,当发现换行符时,数据将传输到文件(或终端)

如果一个流连接到一个终端,就像标准输出的情况一样,它是行缓冲的。因此,这就是您的情况:当您按enter键时,换行符
\n
将导致(行)缓冲区写入标准输出。但是,当调用
write(2)
时,会绕过libc用户缓冲,并将数据写入相应的文件描述符(STDOUT\u FILENO)

因此,正如我前面所说,
write(2)
是一个系统调用;但实际上,当您调用
write
时,您是在调用系统调用的库包装器,它处理系统调用遵循的严格协议(例如,它期望参数的位置等)

顺便说一下,我在这里所说的一切都可以在
putchar(3)
write(2)
setbuf(3)
的手册页中找到。括号中的数字指的是手册中的章节:
2
用于系统调用,
3
用于库函数(
man
应提供章节及其主题的列表)


希望有帮助。

你可以提到
setvbuf(stdout,0,_IONBF,0)
作为一种让
putchar()
立即回显的方法。或者我做了,这就足够了。(您可能想做点什么来说服您,您在输出上看到的字符不是终端通常看到的回声,例如使用
putchar('[')、putchar(c)、putchar(']');
代替
putchar(c);
)您可以提到
setvbuf(stdout,0,_IONBF,0)
作为让
putchar()
立即回显的一种方式。或者我做了,这就足够了。(您可能想做点什么来说服您,您在输出上看到的字符不是终端通常看到的回声,例如使用
putchar('[')、putchar(c)、putchar(']');
代替
putchar(c);