如何仅用标准C库实现kbhit()功能?需开发Pong游戏
用标准C实现非阻塞键盘输入(替代kbhit())
标准C库本身没有提供非阻塞键盘检测的原生函数,不过可以借助平台相关的终端控制API,封装出和kbhit()功能一致的函数。以下分平台给出实现方案:
Windows平台实现
通过Windows控制台API检测输入事件,判断是否有按键按下:
#include <stdio.h> #include <windows.h> int my_kbhit(void) { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD dwEvents; INPUT_RECORD irInput; // 检查是否存在未处理的输入事件 if (!GetNumberOfConsoleInputEvents(hStdin, &dwEvents) || dwEvents == 0) { return 0; } // 读取事件但不从缓冲区移除 if (!PeekConsoleInput(hStdin, &irInput, 1, &dwEvents)) { return 0; } // 判断是否为按下键盘的事件 if (irInput.EventType == KEY_EVENT && irInput.Event.KeyEvent.bKeyDown) { // 清空当前输入缓冲区,避免重复检测同一按键 FlushConsoleInputBuffer(hStdin); return 1; } // 若不是键盘事件,移除该事件避免干扰后续输入 ReadConsoleInput(hStdin, &irInput, 1, &dwEvents); return 0; }
Linux/Unix平台实现
通过修改终端属性,将行缓冲模式改为非阻塞模式,实现按键检测:
#include <stdio.h> #include <termios.h> #include <fcntl.h> #include <unistd.h> int my_kbhit(void) { struct termios oldt, newt; int ch; int oldf; // 保存当前终端属性 tcgetattr(STDIN_FILENO, &oldt); newt = oldt; // 禁用规范模式(行缓冲)和回显 newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); // 设置标准输入为非阻塞模式 oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); // 尝试读取一个字符,无输入时返回EOF ch = getchar(); // 恢复终端原有属性和模式 tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); // 若读取到字符,放回输入缓冲区供后续读取 if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; }
跨平台封装版本
利用条件编译整合两个平台的实现,一次编写多平台可用:
#include <stdio.h> #ifdef _WIN32 #include <windows.h> int my_kbhit(void) { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD dwEvents; INPUT_RECORD irInput; if (!GetNumberOfConsoleInputEvents(hStdin, &dwEvents) || dwEvents == 0) { return 0; } if (!PeekConsoleInput(hStdin, &irInput, 1, &dwEvents)) { return 0; } if (irInput.EventType == KEY_EVENT && irInput.Event.KeyEvent.bKeyDown) { FlushConsoleInputBuffer(hStdin); return 1; } ReadConsoleInput(hStdin, &irInput, 1, &dwEvents); return 0; } #else #include <termios.h> #include <fcntl.h> #include <unistd.h> int my_kbhit(void) { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } #endif
注意事项
- Windows平台编译时,编译器通常会自动链接
kernel32.lib,若出现链接错误,需手动指定该库。 - Linux/Unix平台无需额外链接库,直接用
gcc编译即可。 - 该函数仅检测是否有按键按下,若要读取按键内容,可在
my_kbhit()返回1后调用getchar()或对应平台的键盘读取函数。
内容的提问来源于stack exchange,提问作者Mouad Hamrani
相关产品推荐
相关产品推荐

