Почему 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() вернется только после нажатия Enter, это не то, что я хочу.

2 ответа

В Linux getch() вероятно curses функция, которая сильно отличается от специфичной для Windows функции с тем же именем. curses (ncurses), вероятно, излишне для того, что вы хотите сделать.

Самый простой подход - подождать, пока пользователь нажмет 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 является часто задаваемым вопросом. На самом деле, это вопрос 19.1 в FAQ по comp.lang.c.

РЕДАКТИРОВАТЬ 2: Я не очень много работал с проклятиями в последнее время, но я просто немного поиграл с этим. Похоже, что getch() не будет работать, если вы сначала не позвоните initscr() -- а также initscr() очищает экран. curses предназначен для использования с такими приложениями, как текстовые редакторы, которым требуется полный контроль над дисплеем. Там может быть способ использовать getch() без контроля над экраном, но я не нашел его.

system("stty ...") Kludge может быть лучшим подходом.

EDIT3: termios решение в другом ответе наверное лучшее (system("stty ...") проще, но вызов внешней программы кажется излишним.

Что касается комментария ОП "Я не могу поверить Press any key to exit. это так хлопотно делать в с ", да, это кажется странным - но если подумать, есть веские причины для этого.

Посмотрите на программы, установленные в типичной системе Unix или Linux. Я думаю, вы обнаружите, что очень немногие из них требуют такого ввода (ожидая одного нажатия клавиши).

Многие программы работают с аргументами командной строки и данными, прочитанными из файлов или из stdin, Все, что пользователь вводит, является входными данными, а не командами или ответами на приглашения.

Некоторые программы запрашивают подтверждение некоторых действий (например, установщики apt-get а также cpan часто это делают) - но они обычно читают строку ввода и проверяют первый символ. Или, для некоторых радикальных действий, они могут потребовать, чтобы вы ввели целое слово "да", а затем Enter (вы не хотите переформатировать свой жесткий диск, потому что вы случайно нажали клавишу).

Конечно, многие программы (текстовые редакторы, средства просмотра файлов) читают односимвольный ввод без эха, но такие программы, как правило, основаны на проклятиях; они контролируют все окно терминала.

Наконец, ряд программ имеют графические интерфейсы (веб-браузеры и т. Д.); они, вероятно, даже не читают со стандартного ввода.

Большинство производственных программ Unix не используют или не нуждаются Press any key to exit подсказки. Они просто выполняют свою работу, часто молча, а затем прекращают работу, чтобы вы могли сделать следующее. Потребность существует в основном в относительно элементарных программах, таких как домашние задания. Не то, чтобы с домашними заданиями что-то не так, но система в целом не предназначена для поддержки такого использования.

Вот минимальный пример

#include <curses.h>

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

Но, кажется, нет никакого способа использовать ncurses без очистки экрана. Пятно перегиба, возможно?!

редактировать

Вот еще один способ, которым я получил работу. Это не использует проклятия и не очищает экран.

#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;
Другие вопросы по тегам