You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅用标准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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:02:15