为什么 getchar 像缓冲区一样工作,而不是像预期的那样实时工作

Why does getchar work like a buffer instead of working as expected in real-time

这是我关于 Whosebug 的第一个问题。如果我没有正确搜索,请原谅我,但我似乎找不到对此的解释。只是尝试从 Bjourne Stroustroup 的论文中举一个例子。添加了我的位以查看数组在我键入文本时重新调整大小。

但好像不是这样的! getchar() 只是等到我输入完所有字符,然后它会执行循环。按照逻辑,它实际上并没有进入循环,获取一个角色,执行它的动作然后迭代。我想知道这是特定于实现的还是打算像这样?

我在 Ubuntu 14.04 LTS 上使用 Codeblocks 和 gcc 4.8.2。如果重要的话,源代码在 cpp 文件中。

while(true)
{
    int c = getchar();
    if(c=='\n' || c==EOF)
    {
        text[i] = 0;
        break;
    }
    text[i] = c;

    if(i == maxsize-1)
    {
        maxsize = maxsize+maxsize;
        text = (char*)realloc(text,maxsize);
        if(text == 0) exit(1);
        cout << "\n Increasing array size to " << maxsize << endl;
    }

    i++;
}

输出结果如下:

数组大小现在是:10 请输入一些文本:这是一些示例文本。我希望看到内存在这里被重新分配,但显然它不是这样工作的!

将数组大小增加到 20

将数组大小增加到 40

将数组大小增加到 80

将数组大小增加到 160

您输入了:这是一些示例文本。我希望看到内存在这里被重新分配,但显然它不是这样工作的!

数组大小现在是:160

这与getchar没有直接关系。 "problem" 是底层终端,它将缓冲您的输入。按下回车键后,输入将发送到程序。在 Linux 中(不知道 Windows 中是否有办法)你可以通过调用

来解决这个问题
/bin/stty raw

在终端中或通过调用

system ("/bin/stty raw");

在你的程序中。这将导致 getchar 立即 return 将输入的字符提供给您。

不要忘记通过调用

重置 tty 行为
/bin/stty cooked

完成后!

这是一个例子(Linux):

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

using namespace std;

int main() {
    system ("/bin/stty raw");
    char c = getchar();
    system ("/bin/stty cooked");
    return 0;
}

也看看这个 SO Post:How to avoid press enter with any getchar()

此外,正如评论中所建议的,请看这里:http://linux.die.net/man/3/termios 尤其是命令 tcsetattr,它应该可以跨平台工作。

实际上,tcsetattr 不适用于 Windows(即本网站中通常所说的 "cross-platform")。但是,问题被标记为 Linux,因此 "cross-platform" 是一个有争议的问题。

默认情况下,标准输入、输出和错误流设置为

  • 行缓冲(输入)
  • 块缓冲(输出)
  • 行缓冲(错误)

您可以更改 that 使用 setbuf, but of course will not solve the problem (the answer calls for single-character input). POSIX terminal I/O (termios) 允许您通过系统调用更改使用 stty 显示的任何标志。通常,您可以直接从脚本调用 stty,很少从 C 程序调用。

读取单个字符是一个常见问题,例如

您也可以使用 ncurses: filter function is useful for programs that process a command-line (rather than a full-screen application). There is a sample program in ncurses-examples (filter.c) 来执行此操作。