C语言 为什么std[in,out]即使连接到终端也没有行缓冲?

axzmvihb  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(77)

根据unix advanced programming

We can see that the default for this system is to have standard input and standard
output line buffered when they’re connected to a terminal.

从这个例子:

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

long buffer_size(FILE *fp)
{
    return (fp->_IO_buf_end - fp->_IO_buf_base);
}

int is_linebf(FILE *fp)
{
    return (fp->_flags & _IOLBF);
}

int is_unbf(FILE *fp)
{
    return (fp->_flags & _IONBF);
}

void stream(const char *name, FILE *fp)
{
    printf("stream = %s, ", name);
    if (is_unbf(fp))
    {
        printf("unbuffered");
    }
    else if (is_linebf(fp))
    {
        printf("line buffered");
    }
    else
    {
        printf("fully buffered");
    }
    printf(", buffer size = %ld\n", buffer_size(fp));
}

int main()
{
    FILE *fp;

    fputs("enter any character: ", stdout);
    if (getchar() == EOF)
    {
        perror("getchar error");
    }
    fputs("one line to standard error\n", stderr);

    stream("stdin", stdin);
    stream("stdout", stdout);
    stream("stderr", stderr);

    if ((fp = fopen("/etc/passwd", "r")) == NULL)
    {
        perror("fopen error");
    }
    if (fgetc(fp) == EOF)
    {
        perror("fgetc error");
    }
    stream("/etc/passwd", fp);
    exit(0);
}

如果我运行它:

$ ./a.out
enter any character: a
one line to standard error
stream = stdin, fully buffered, buffer size = 1024
stream = stdout, fully buffered, buffer size = 1024
stream = stderr, unbuffered, buffer size = 1
stream = /etc/passwd, fully buffered, buffer size = 4096

即使将std[in,out]连接到tty,它们也是 * 完全缓冲 * 而不是 * 行缓冲 *。到底是书写错了,还是我错过了什么?
uname -a

Linux 5.8.0-44-generic #50-Ubuntu SMP Tue Feb 9 06:29:41 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

书中的例子使用了4个操作系统(solaris,openBSD,Linux和macos)的特定于操作系统的宏,所以我的操作系统也在其中。
编辑:在 my 机器中,宏和FILE结构的实现:

  • <stdio.h>-
/* The possibilities for the third argument to `setvbuf'.  */
#define _IOFBF 0        /* Fully buffered.  */
#define _IOLBF 1        /* Line buffered.  */
#define _IONBF 2        /* No buffering.  */
  • <struct_FILE.h>-
struct _IO_FILE
{
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */

  /* The following pointers correspond to the C++ streambuf protocol. */
  char *_IO_read_ptr;   /* Current read pointer */
  char *_IO_read_end;   /* End of get area. */
  char *_IO_read_base;  /* Start of putback+get area. */
  char *_IO_write_base; /* Start of put area. */
  char *_IO_write_ptr;  /* Current put pointer. */
  char *_IO_write_end;  /* End of put area. */
  char *_IO_buf_base;   /* Start of reserve area. */
  char *_IO_buf_end;    /* End of reserve area. */

  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
  int _flags2;
  __off_t _old_offset; /* This used to be _offset but it's too small.  */

  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
  __off64_t _offset;
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};

FILE就是typedef struct _IO_FILE FILE

vuv7lop3

vuv7lop31#

上一点:
正如注解中所述,帮助函数可能无法准确地描述系统文件缓冲的真实的默认状态。
也就是说,文件缓冲是实现定义的,从C11 N1570:

§7.21.3

3 - [...] * 当流被完全缓冲时,当缓冲区被填满时,字符将作为块传输到主机环境或从主机环境传输。如果流是行缓冲的,则当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。此外,当缓冲器被填充时,当在未缓冲流上请求输入时,或者当在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块被传输到主机环境。对这些特性的支持是实现定义的,可能会通过setbuf和setvbuf函数受到影响。*
7 - * 在程序启动时,三个文本流是预定义的,不需要显式打开-标准输入(用于阅读常规输入),标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。初始打开时,标准错误流未完全缓冲;当且仅当标准输入和标准输出流可以被确定为不涉及交互式设备时,该标准输入和标准输出流被完全缓冲。
我强调的是。
根据我在Linux和MacOS上的个人经验,stdinstdout在与终端关联时是行缓冲的,否则是全缓冲的,而stderr是无缓冲的,所以这与书中描述的行为一致,显然不同于您的特定情况,但同样,这是实现定义的。
我不会说这本书是错的,但 * 默认行为 * 绝对可以解释为这是规范,并始终适用于上述系统。

相关问题