getchar()和缓冲区顺序

5
我正在阅读一本初学者的C语言书,关于getchar()和缓冲区顺序(特别是与换行符有关)让我感到困惑。它说:
“摆脱回车键的按下是所有初学者C程序员必须面对的问题。考虑以下程序片段:”
printf("What are your two initials?\n");
firstInit = getchar();
lastInit = getchar();

你可能认为,如果用户键入了GT,那么G将进入变量firstInit,而T将进入lastInit,但事实并非如此。第一个getchar()要等到用户按下Enter键才会结束,因为G会进入缓冲区。只有当用户按下Enter键时,G才离开缓冲区并进入程序,但是Enter键仍然停留在缓冲区中!因此,第二个getchar()将该Enter键(\n)发送给了lastInit。如果有后续的getchar()T仍然会留下来。
首先,我不理解作者为什么要解释为什么\n会发送给lastInit,而不是T。我猜是因为我将缓冲区视为“先进先出”的方式。不管怎样,我都不理解作者所呈现的顺序背后的逻辑——如果用户输入G,然后是T,最后是Enter键,为什么G被第一个getchar()捕获,Enter键(换行符)被第二个getchar()捕获,而T被第三个getchar()捕获?令人困惑。
其次,我也尝试了一下(在Windows 8.1的VMware虚拟机上运行Ubuntu 14.04,使用Code::Blocks文本编辑器和gcc编译器),并且得到了与作者所说的看起来不合理的结果!G进入了firstInitT进入了lastInit。以下是我的代码:
#include <stdio.h>

main()
{
  char firstInit;
  char lastInit;

  printf("What are your two initials?\n");

  firstInit = getchar();
  lastInit = getchar();

  printf("Your first initial is '%c' and ", firstInit);
  printf("your last initial is '%c'.", lastInit);

  return 0;
}

输出结果为:

你的两个首字母是什么?
GT
你的第一个首字母是 'G',最后一个首字母是 'T'。

我还创建了一个后续程序,似乎证实换行符最后出现在缓冲区中:

main()
{
  char firstInit;
  char lastInit;
  int newline;

  printf("What are your two initials?\n");

  firstInit = getchar();
  lastInit = getchar();
  newline = getchar();

  printf("Your first initial is '%c' and ", firstInit);
  printf("your last initial is '%c'.", lastInit);
  printf("\nNewline, ASCII %d, comes next.", newline);

  return 0;
}

输出结果如下:

你的两个首字母是什么?
GT
你的第一个首字母是'G',最后一个首字母是'T'。
接下来是换行符,ASCII码为10。

那么我是否漏掉了什么或者作者是错误的呢?(或者这取决于编译器,即使作者没有这样说)?

书籍:C Programming Absolute Beginner's Guide,第三版,Greg Perry,©2014,Ubuntu 14.04,gcc编译器版本4.8.4


请尝试输入“G<换行>T<换行>”。 - Andrew Henle
@AndrewHenle 我只能在程序终止之前输入 G<newline>。在这种情况下,firstInit 捕获了 G,lastInit 捕获了换行符,这正是我所期望的。 - yroc
1
只需键入“GT <Enter>”。顺便说一句,main 定义不正确,应该是 int main(void) - Weather Vane
2
没错。你基本上证实了你的书是错误的。我不知道任何情况下它会正确,但很难证明在作者写作时它不正确。 - Andrew Henle
你写道:“我得到了与作者所说的不可能发生的结果!”那这本书还有什么用? - Weather Vane
显示剩余10条评论
2个回答

3
作者正在描述用户输入“G<enter>T<enter>”的情况。

这肯定可以解释,你可能是对的,这可能是作者的意图。但是在我看来措辞有误导性:“...如果用户键入GT”并不等同于“...如果用户键入G<enter>T<enter>。”谢谢。 - yroc

2

这是一个不同的例子。菜单循环将针对每个输入的选择1、2或3迭代两次。
去掉//while ( ( getchar ( ) != '\n')) {}中的注释//,并且在每次输入后清除缓冲区,菜单将只循环一次。

#include <stdio.h>

int main()
{
    char *menu[] = { "1. first", "2. second", "3. third", "4. Exit"};
    int choice = 0;
    int each = 0;

    do {
        for ( each = 0; each < 4; each++) {
            printf ( "%s\n", menu[each]);
        }
        printf ( "\tenter choice 1-4\n");
        choice = getchar ( );
        //clear the buffer
        //while ( ( getchar ( ) != '\n')) {}
        printf ( "you entered %d %c\n", choice, choice);
    } while ( choice != '4');
    return 0;
}

谢谢,我会尝试一下! - yroc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接