当 stdin 缓冲区大小设置为 0 时,C 在何处存储输入?

Where does C store input when stdin buffer size is set to 0?

在尝试弄清楚 C I/O 处理的一些内部结构时,我偶然发现了 stdio 缓冲区的一些奇怪行为。

如果我将 stdin 缓冲区大小设置为 0 并在调用 getchar 函数后输入多个字符,那么不仅第一个 getchar 将 return 第一个输入值,而且以下所有的也是(假设提供了足够的字符)。此外,如果我们在每次 getchar 调用后输出存储在 stdin 中的值,我们可以看到在每次其他调用之后,stdin 缓冲区将具有赋予前一个 getchar 的值(即对于输入 12345,我们将得到 stdin: 2 stdin: stdin: 4 标准输入:作为输出)。

setvbuf(stdin, NULL, _IONBF, 0);
char inpChar1 = getchar();
printf("stdin: %s \n", *stdin);
char inpChar2 = getchar();
printf("stdin: %s \n", *stdin);
char inpChar3 = getchar();
printf("stdin: %s \n", *stdin);
char inpChar4 = getchar();
printf("stdin: %s \n", *stdin);
char inpChar5 = getchar();
printf("%c %c %c %c %c", inpChar1, inpChar2, inpChar3, inpChar4, inpChar5);

为什么下面的代码会这样工作? 我的疯狂猜测是还有另一个缓冲区存储这些值,但我目前不知道如何找到它或者它是否存在。

C 没有存储它。如果 stdin 是常规文件,则基础文件位置将停留在 stdin 的逻辑 stdio 位置所在的位置。

如果 stdin 是终端,则 "underlying file position"(注意:它不可搜索,所以它不是真正的位置,但在某种意义上也适用相同的概念)是操作系统中的位置(或者,在裸机上,16550 UART 的硬件 FIFO 或其他型号上的类似物)输入缓冲区。每次你调用 fgetc,它会从那里读取另一个字节,直接进入 return 值传递回你的程序,C [library] 实现没有缓冲。

您应该阅读 C 运行时的文档。例如一些运行时,比如 IBM 运行时明确指出:

_IONBF No buffer is used.

所以很明显读取操作return一次一个字符.

来自上面的文档:

The setvbuf() function has no effect on stdout, stdin, or stderr.

您的函数调用在此运行时无效

根据请求更新

OP 说他使用 Microsoft 产品。这是来自 Microsoft 的相关 documentation:

_IONBF No buffer is used, regardless of arguments in call to setvbuf.

根据你的具体情况,答案是:

读取操作一次读取一个字符。您的 setvbuf 函数调用不会影响 stdin 缓冲区大小。

stdin 是指向依赖于实现的 FILE 结构的指针。您不能将它用作字符指针,并且 Visual C 应该在警告级别 -W4 左右警告类型不匹配。这样使用是未定义的行为,结果是没有意义的。

可以想象该结构在某处包含一个缓冲区或最后读取的字符,但如果不查看库源就无法知道是否可用 and/or 阅读文档,当一个字符时应该转换为字符指针以这种方式使用它,当一个人知道第一个元素是一个字符(-array)时。在任何情况下,这样的代码都是不可移植的。

这是一个工作代码示例,它显示了它在 Linux 系统上的工作原理。您可以从所有 readwrite 调用中看到没有缓冲区。它一次读取和写入一个。

$ cat c-read-buffer-test.c

#include <stdio.h>

int main() {
  char input[8] = {0};
  const size_t input_len = sizeof input;
  size_t i;
  int inC;

  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);

  for (i = 0; i < input_len; ++i) {
    inC = getchar();
    input[i] = inC;
  }

  for (i = 0; i < input_len; ++i) {
    if (i > 0)
      printf(" ");
    printf("%c", input[i]);
  }
  printf("\n");
  return 0;
}

$ echo abcdefgh | strace ./c-read-buffer-test 

execve("./c-read-buffer-test", ["./c-read-buffer-test"], 0x7fffd97152e0 /* 61 vars */) = 0
brk(NULL)                               = 0x48a20000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=197658, ...}) = 0
mmap(NULL, 197658, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fffab5b0000
close(3)                                = 0
openat(AT_FDCWD, "/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]n[=10=][=10=][=10=][=10=][=10=][=10=]"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=882496, ...}) = 0
mmap(NULL, 279840, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fffab560000
mmap(0x7fffab590000, 131072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0x7fffab590000
close(3)                                = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]0P[=10=][=10=][=10=][=10=][=10=]"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=6723976, ...}) = 0
mmap(NULL, 2118520, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fffab350000
mmap(0x7fffab540000, 131072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7fffab540000
close(3)                                = 0
mprotect(0x7fffab540000, 65536, PROT_READ) = 0
mprotect(0x7fffab590000, 65536, PROT_READ) = 0
mprotect(0x10010000, 65536, PROT_READ)  = 0
mprotect(0x7fffab640000, 65536, PROT_READ) = 0
munmap(0x7fffab5b0000, 197658)          = 0
set_tid_address(0x7fffab653110)         = 58832
set_robust_list(0x7fffab653120, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7fffab566630, sa_mask=[], sa_flags=SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7fffab566740, sa_mask=[], sa_flags=SA_RESTART|SA_SIGINFO}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
read(0, "a", 1)                         = 1
read(0, "b", 1)                         = 1
read(0, "c", 1)                         = 1
read(0, "d", 1)                         = 1
read(0, "e", 1)                         = 1
read(0, "f", 1)                         = 1
read(0, "g", 1)                         = 1
read(0, "h", 1)                         = 1
write(1, "a", 1a)                        = 1
write(1, " ", 1 )                        = 1
write(1, "b", 1b)                        = 1
write(1, " ", 1 )                        = 1
write(1, "c", 1c)                        = 1
write(1, " ", 1 )                        = 1
write(1, "d", 1d)                        = 1
write(1, " ", 1 )                        = 1
write(1, "e", 1e)                        = 1
write(1, " ", 1 )                        = 1
write(1, "f", 1f)                        = 1
write(1, " ", 1 )                        = 1
write(1, "g", 1g)                        = 1
write(1, " ", 1 )                        = 1
write(1, "h", 1h)                        = 1
write(1, "\n", 1
)                       = 1
exit_group(0)                           = ?
+++ exited with 0 +++