必须分平台处理:windows用_getch()(需,不回显、不等待回车,扩展键需调两次);linux/macos用termios禁用icanon和echo,并注意保存恢复终端设置。

直接用 std::cin、getchar() 或 std::getline() 都做不到——它们全依赖系统行缓冲,必须按回车才吐数据,且默认回显。真要“按键即得、不显示”,得绕过标准流,分平台处理。
Windows 下用 _getch() 最快上手
这是最省事的方案:不等待回车、不回显、单字符立即返回。但它是 MSVC/MinGW 的 CRT 扩展,非标准,需包含 <conio.h></conio.h>。
-
_getch()返回int,不是char;读到普通键(如 'A')直接返回 ASCII 值 - 方向键、F1–F12 等扩展键会分两次返回:第一次是
0或0xE0,第二次才是真实扫描码,必须连调两次 - Ctrl+C 会被当作
'\x03'正常返回,不会触发中断;如需捕获 Ctrl+C,得另配signal() - 别和
_kbhit()混用时漏判:先_kbhit()返回非零再_getch(),否则可能阻塞(虽然文档说不阻塞,但某些 MinGW 版本有 bug)
Linux/macOS 下靠 termios 关掉行缓冲和回显
POSIX 没有 _getch(),只能手动改终端属性。核心就三件事:禁 ICANON(关行缓冲)、禁 ECHO(关回显)、设 c_cc[VMIN] = 1; c_cc[VTIME] = 0(读到 1 字节就返)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 必须用
tcgetattr(STDIN_FILENO, &old)先存原始配置,程序退出前用tcsetattr(STDIN_FILENO, TCSANOW, &old)恢复,否则终端会变“哑巴”(输命令不显示、回车没反应) - 方向键、ESC 键等发的是多字节序列(如 ← 是
"\033[D"),read(STDIN_FILENO, &c, 1)每次只读一个字节,得自己缓存+解析,不能当单字符处理 - 重定向输入时(如
./a.out ),<code>tcsetattr()会失败,得提前检查isatty(STDIN_FILENO),失败则 fallback 到普通read() - 若想非阻塞(比如轮询时不卡住),可设
c_cc[VMIN] = 0; c_cc[VTIME] = 0,此时无输入时read()立即返回 0,但要注意 errno 可能是EAGAIN,不能当错误直接退出
跨平台封装别碰 std::cin,用预处理器分发
别幻想用 std::cin.sync() 或 fflush(stdin) 补救——C++ 标准里 fflush(stdin) 行为未定义,cin.sync() 在多数编译器对 stdin 无效。唯一靠谱路径是写两套:
- Windows 侧用
GetAsyncKeyState()(查物理键状态,适合快捷键响应)或_getch()+_kbhit()(适合“按任意键继续”) - Unix 侧用
termios+read(),并统一处理 ESC 序列(比如把"\033[A"映射为-1,"\033[B"为-2) - 函数接口统一成
bool kbhit()和int getch(),内部用#ifdef _WIN32/#ifdef __linux__分支 - 容易被忽略的是异常退出路径:信号(如 SIGINT)或抛异常时,termios 设置可能没机会恢复,建议用 RAII 封装,或在 signal handler 里强制恢复
真正难的不是读到一个键,而是读完之后终端还能正常用——termios 配置一旦没恢复,整个 shell 就废了;_getch() 读扩展键漏第二次调用,方向键就永远卡住。这些坑不在代码里报错,而是在你关掉程序后才露脸。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










