C语言中kbhit()如何检测键盘输入?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

kbhit() 是什么?

kbhit() 是一个非标准库函数,它的全称是 "keyboard hit"(键盘敲击),它的主要作用是检查当前是否有键盘按键被按下

关键特性:

  • 非阻塞式:它不会等待用户按键,如果调用时没有按键被按下,它会立即返回 0,如果有一个按键被按下,它会返回一个非零值(通常是按键的 ASCII 码),并且不会消耗这个按键(即按键仍然留在输入缓冲区中)。
  • 主要用于控制台程序:在像 Windows 的命令提示符 或 Linux/macOS 的终端 这样的控制台环境中非常有用。
  • 实现不统一:由于它不是 C 标准库(如 stdio.hstdlib.h)的一部分,它的实现和头文件在不同的操作系统上是不同的。

为什么需要 kbhit()

在标准的 C 程序中,当你使用 scanf()getchar() 来读取输入时,程序会阻塞(暂停执行),直到用户真正按下回车键。

场景示例: 你想制作一个简单的游戏,比如一个左右移动的小方块,如果用 scanf,你的代码会像这样:

#include <stdio.h>
int main() {
    int position = 0;
    char input;
    printf("Use 'a' and 'd' to move. Press 'q' to quit.\n");
    while (1) {
        // 程序会在这里停下来,等待你输入一个字符并按回车
        scanf(" %c", &input); 
        if (input == 'q') {
            break;
        } else if (input == 'a') {
            position--;
        } else if (input == 'd') {
            position++;
        }
        printf("Position: %d\n", position);
    }
    return 0;
}

这个程序的问题是,每移动一次,你都必须按一次回车,这非常不流畅。

kbhit() 的解决方案: 使用 kbhit(),你可以创建一个游戏循环,这个循环可以一直运行,不断地检查是否按下了某个键,如果按下了,就执行相应的动作;如果没有,就继续做其他事情(比如更新画面、计算逻辑等),从而实现实时响应


如何在不同平台上使用 kbhit()

因为 kbhit() 不是标准函数,所以你需要根据你的操作系统来选择正确的实现方式。

A. 在 Windows 平台上

在 Windows 中,kbhit() 函数是标准 C 运行时库的一部分。

  • 头文件: <conio.h>
  • 函数原型: int kbhit(void);
  • 返回值:
    • 如果有按键被按下,返回一个非零值(通常就是按键的 ASCII 码)。
    • 如果没有按键被按下,返回 0
  • 辅助函数: 通常会和 getch() 配合使用。getch() 也是 <conio.h> 中的函数,它会从键盘读取一个字符,并且不显示在屏幕上,也不会等待回车。

示例代码 (Windows):

#include <stdio.h>
#include <conio.h> // Windows 专用头文件
int main() {
    printf("Press any key to continue, or ESC to quit...\n");
    while (1) {
        // 检查是否有按键被按下
        if (kbhit()) {
            // 如果有,获取按键的 ASCII 码
            int key = getch();
            // 如果按下的是 ESC 键 (ASCII 27),则退出循环
            if (key == 27) {
                break;
            }
            // 打印按下的键
            printf("You pressed: %c (ASCII: %d)\n", key, key);
        }
        // 如果没有按键,这里可以做其他事情,比如显示一个等待提示
        // _sleep(100); // 暂停 100 毫秒,避免 CPU 占用过高
    }
    printf("Program exited.\n");
    return 0;
}

B. 在 Linux / macOS 平台上

在 Linux 和 macOS 的标准 C 库中没有 kbhit() 函数,你需要自己手动实现它,通常的方法是使用 POSIX 标准中的 <termios.h><unistd.h> 来修改终端的属性,使其进入“原始模式”(raw mode),然后使用 select()poll() 来检查文件描述符(标准输入 STDIN_FILENO)是否可读。

实现思路:

  1. 保存终端的当前设置。
  2. 将终端设置为“原始模式”,这样按键就不会被立即显示,也不会被缓冲(直到回车)。
  3. 使用 select() 函数来检查 stdin 是否有数据可读(即是否有按键)。
  4. select() 返回有数据,则使用 read() 读取字符。
  5. 程序退出前,恢复终端的原始设置。

示例代码 (Linux/macOS):

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
// 定义 kbhit() 函数
int kbhit() {
    struct timeval tv = {0L, 0L};
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    return select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
}
// 定义 getch() 函数
int getch() {
    struct termios oldt, newt;
    int ch;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}
int main() {
    printf("Press any key to continue, or ESC to quit...\n");
    while (1) {
        if (kbhit()) {
            int key = getch();
            if (key == 27) { // ESC
                break;
            }
            printf("You pressed: %c (ASCII: %d)\n", key, key);
        }
        // usleep(10000); // 暂停 10 毫秒
    }
    printf("Program exited.\n");
    return 0;
}

编译命令 (Linux/macOS):

gcc your_program.c -o your_program

跨平台解决方案

如果你想让你的代码同时在 Windows 和 Linux/macOS 上运行,你可以使用 #ifdef 预处理指令来根据操作系统选择不同的实现。

示例代码 (跨平台):

#include <stdio.h>
#ifdef _WIN32
    // Windows 平台
    #include <conio.h>
#else
    // Linux / macOS 平台
    #include <unistd.h>
    #include <termios.h>
    #include <sys/select.h>
    int kbhit() {
        struct timeval tv = {0L, 0L};
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(STDIN_FILENO, &fds);
        return select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
    }
    int getch() {
        struct termios oldt, newt;
        int ch;
        tcgetattr(STDIN_FILENO, &oldt);
        newt = oldt;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);
        ch = getchar();
        tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
        return ch;
    }
#endif
int main() {
    printf("Press any key to continue, or ESC to quit...\n");
    while (1) {
        if (kbhit()) {
            int key = getch();
            if (key == 27) { // ESC
                break;
            }
            printf("You pressed: %c (ASCII: %d)\n", key, key);
        }
    }
    printf("Program exited.\n");
    return 0;
}

特性 描述
功能 检查键盘是否有按键被按下,但不等待。
类型 非标准库函数
Windows 使用 <conio.h>,直接可用。
Linux/macOS 需要手动使用 <termios.h><unistd.h> 实现。
用途 创建实时交互的控制台程序、游戏、工具等。
搭档 通常与 getch() 配合使用,以获取按下的具体字符。

kbhit() 是一个非常实用的工具,但它也提醒我们 C 语言在不同平台上的差异性,编写可移植的代码需要对这些差异有所了解。

-- 展开阅读全文 --
头像
c语言defaults
« 上一篇 04-17
dede如何不调用自身数据实现内容展示?
下一篇 » 04-17

相关文章

取消
微信二维码
支付宝二维码

目录[+]