使用scanf()和getchar()处理输入文字很合适,它会停止后续代码执行等待用户输入,直到按回车才继续执行,这种缓冲方式称为阻塞式监听,很多时候我们利用在代码末尾添加getchar()来防止程序马上退出以看到输出结果。但这种方式对于编写需要实时响应按键的程序简直是灾难,例如用键盘操作的游戏。我们需要一种随时获取键盘按下状态的非缓冲机制,这个机制称为非阻塞式监听。在windows中C库给我们准备了三个函数来干这件事情:
kbhit() 查看按键缓冲区中是否有字符,有返回true,没有返回false,非阻塞函数。
getche() 如果按键缓冲区中有字符则取出并返回该字符,没有则等待用户输入,阻塞函数
getch() 如果按键缓冲区中有字符则取出并返回该字符,没有则等待用户输入,该字符不能被显示,阻塞函数
当按下键盘上的按键时,kbhit()并不能阻止字符送入缓冲区,但kbhit()随时可以检测缓冲区是否有内容,getche()和getch()与getchar()一样也是阻塞式的,用于从按键缓冲区取出字符,如果有内容立即取出,如果没有内容则等待用户输入,不同的是输入后不必等到按回车才从缓冲区读取字符,而是立即取出,这就为创造非阻塞式监听提供了方案,请看下面代码:
#include<stdio.h>
#include<windows.h>
#include<conio.h>
void onKeyDown(char c)
{
printf("pressedkey=%d\n", c);
if (c == 27) exit(0);
}
void onKeyUp(char c)
{
printf("releasedkey=%d\n", c);
}
int main()
{
char key = 0;
while (1)
{
if (kbhit())
{
key = getch();
onKeyDown(key);
}
else if(key)
{
onKeyUp(key);
key = 0;
}
Sleep(100);
}
return 0;
}
测试程序也请使用windows自身的控制台,有些IDE自带的控制台可能会工作不正常,它们跟原生控制台还是有区别的。这段代码在main()函数中使用死循环模拟了一个间隔事件调用程序,这也是基于事件驱动的高级程序的原始实现方式。在循环体中不断检测按键缓冲区中的内容,当按下一个键盘按键时,输入缓冲区中的字符立即被hbhit()侦测到,接着立刻被getche()取出,然后调用回调函数onKeyDown()触发事件,该事件将按键字符作为事件参数发送。在事件onKeyDown中传入键入字符的ascii码,如果输入的是ESC键退出程序。
如果不想用死循环可以修改代码如下:
#include<stdio.h>
#include<windows.h>
#include<conio.h>
void onKeyDown(char c)
{
printf("\nkey=%d\n", c);
}
int main()
{
char c=0;
do
{
c = getche();
onKeyDown(c);
}
while (c != 27);
return 0;
}
上面代码仍然使用阻塞监听,由于输入字符无需回车,效果与死循环相同,但不能检测到键盘释放事件。非缓冲按键并不是C语言定义的标准,因为有些设备和操作系统并不支持非缓冲输入,kbhit(),getche(),getch()是windows提供的3个函数,不能用于其它操作系统,kbhit()需导入windows库,getche()和getch()需导入conio.h库。
输入密码
当我们在控制台中键入字符时字符会显示出来,如果是密码就会被剽窃,这时可以使用getch()函数来获取输入字符,它不会显示在控制台中。修改上面的代码,在onKeyDown事件中获取字符来验证输入密码,如下:
#include<stdio.h>
#include<windows.h>
#include<conio.h>
#include<string.h>
char password[7] = "123456";
char pwStr[7] = "abcdef";
int i = 0;
void onKeyDown(char c)
{
if (c == 13) //键入回车键
{
if (i == 6 && strcmp