为什么getch()在按任意键之前返回?

时间:2011-09-14 01:52:53

标签: c linux getch

int main(int argc, char *argv[], char *env[])
{
    printf("Press any key to exit.\n");
    getch();
    return 0;
}

根据手册页,

  

getch应该等到按任意键

...但实际上它在按任意键之前直接返回。 (返回的值为-1)。

为什么?


更新

我在Linux上。如果不使用Press any key to exit.,我该如何实施getch()

getchar()只会在按回车后返回,这不是我想要的。

2 个答案:

答案 0 :(得分:12)

在Linux上,getch()可能是curses函数,它与同名的Windows特定函数完全不同。 cursesncurses)对于您想要做的事情可能有点过分。

最简单的方法是等到用户按下 Enter ,你可以这样做:

int c;
printf("Press <enter> to quit: ");
fflush(stdout);
while ((c = getchar()) != '\n' && c != EOF) {
    /* nothing */
}

如果您真的希望用户能够按任何键,而不仅仅是 Enter ,您可以执行以下操作:

system("stty cbreak -echo");
getchar();
system("stty cooked echo");

第二个stty旨在将tty恢复到合理的设置(它应该将它们恢复到原来的状态,但保存和恢复状态会稍微复杂一些)。可能有更简洁的方法(使用stty程序本身使用的任何库函数)。

编辑:在不等待 Enter 的情况下阅读单个字符是一个经常被问到的问题。事实上,它是comp.lang.c FAQ中的问题19.1。

EDIT2:我最近没有做过很多关于诅咒的工作,但我只是做了一些游戏。除非您先致电getch()并且initscr()清除屏幕,否则initscr()似乎无效。 curses适用于需要完全控制显示的文本编辑器等应用程序。可能有一种方法可以在不控制屏幕的情况下使用getch(),但我还没有找到它。

system("stty ...") kludge实际上可能是最好的方法。

EDIT3:另一个答案中的termios解决方案可能是最好的(system("stty ...")更简单,但调用外部程序感觉有些过分。

关于OP的评论“我不敢相信Press any key to exit.在c中做的很麻烦”,是的,这看起来确实很奇怪 - 但进一步认为它有正当理由。

查看典型Unix或Linux系统上安装的程序。我想你会发现很少有人需要这种输入(等待一次按键)。

许多程序使用命令行参数和从文件或stdin读取的数据。用户键入的任何内容都是输入数据,而不是命令或对提示的响应。

某些程序会要求确认某些操作(apt-getcpan等安装程序经常执行此操作) - 但他们通常会读取输入的并检查第一个角色。或者,对于一些激烈的操作,他们可能会要求您输入整个单词“是”,然后输入 Enter (您不想重新格式化硬盘驱动器,因为您不小心碰到了密钥)。

当然很多程序(文本编辑器,文件查看器)都读取单字符非回显输入,但这些程序往往是基于curses的;他们控制整个终端窗口。

最后,许多程序都有GUI界面(Web浏览器等);他们甚至可能都没有从stdin读过。

大多数生产Unix程序不使用或需要Press any key to exit提示。他们只是默默地做自己的工作,然后终止,这样你就可以做下一件事了。这种需求主要存在于相对基础的课程中,例如家庭作业。并不是家庭作业有任何问题,但整个系统并非主要用于支持这种用法。

答案 1 :(得分:6)

这是一个最小的例子

#include <curses.h>

int main(){
    initscr();
    cbreak();
    noecho();
    printw("Press any key to continue.");
    refresh();
    getch();
    endwin();
    return 0;
}

但似乎没有任何方法可以在不清除屏幕的情况下使用ncurses。 过度杀戮的污点,也许?!

修改

这是我工作的另一种方式。这不使用curses,也不清除屏幕。

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

int main() {
    struct termios old,new;

    tcgetattr(fileno(stdin),&old);
    tcgetattr(fileno(stdin),&new);
    cfmakeraw(&new);
    tcsetattr(fileno(stdin),TCSANOW,&new);
    fputs("Press any key to continue.",stdout);
    fflush(NULL);
    fgetc(stdin);
    tcsetattr(fileno(stdin),TCSANOW,&old);

    return 0;
}

该联机帮助页称cfmakeraw()可能不是完全可移植的。但它只是设置一大堆旗帜的简写:

Raw mode
    cfmakeraw() sets the terminal to something like the "raw" mode of the old  Version  7  terminal
    driver:  input  is  available character by character, echoing is disabled, and all special pro-
    cessing of terminal input and output characters is disabled.  The terminal attributes  are  set
    as follows:

       termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                       | INLCR | IGNCR | ICRNL | IXON);
       termios_p->c_oflag &= ~OPOST;
       termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
       termios_p->c_cflag &= ~(CSIZE | PARENB);
       termios_p->c_cflag |= CS8;