本节涉及到的部分知识在后续章节才会讲到,如果您是初学者,请阅读完入门教程再来回顾本节。
一个安全的程序在用户输入密码时不应该显示密码本身,而应该回显星号或者点号,例如······或******,这在网页、PC软件、ATM机、POS机上经常看到。但是C语言没有提供类似的功能,控制台上只能原样显示用户输入的字符。
我们完全可以模拟密码输入的效果,请先看下面的代码:
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#define PWDLEN 20
void getpwd(char *pwd, int pwdlen);
int main(){
char pwd[PWDLEN+1];
printf("Input password: ");
getpwd(pwd, PWDLEN);
printf("The password is: %s\n", pwd);
return 0;
}
/**
* 获取用户输入的密码
* @param pwd char* 保存密码的内存的首地址
* @param pwdlen int 密码的最大长度
**/
void getpwd(char *pwd, int pwdlen){
char ch = 0;
int i = 0;
while(i<pwdlen){
ch = getch();
if(ch == '\r'){ //回车结束输入
printf("\n");
break;
}
if(ch=='\b' && i>0){ //按下删除键
i--;
printf("\b \b");
}else if(isprint(ch)){ //输入可打印字符
pwd[i] = ch;
printf("*");
i++;
}
}
pwd[i] = 0;
}
运行结果:
代码中定义了一个函数 getpwd(),它有两个参数:pwd 为保存密码的内存的首地址,pwdlen 为密码的最大长度。
函数通过 while 循环来不断读取用户输入的字符,并逐一对它们进行处理:
while 循环中之所以使用 getch() 来获取字符,是因为该函数既没有回显也没有缓存,可以立即读取到用户输入的字符,并且不会在屏幕上显示出来。
从循环条件 i<pwdlen 可以看出,当用户输入的密码超过最大长度时跳出循环,结束输入。
用户按下回车键时,getchar() 将读取到\n字符,而 getch() 将读取到\r字符。也就是说,对于不同的字符输入函数,回车键产生的字符不同,这个细节读者要引起注意。
密码保存在字符数组中,当用户按下删除键时,不仅要删除前面的星号,还应该删除字符数组中前面的元素。
需要注意的是,数组中的元素在内存中是连续分布的,无法直接删除。上面代码中,我们并没有对数组元素进行任何操作,而是将变量 i 减 1,跳过要删除的字符。这些本该被删除的字符依然留在数组中,它们会被后续输入的字符覆盖掉。
第29行代码中,我们通过printf("\b \b");来删除前面的星号。\b表示退格,也就是光标向后移动一个位置。退格,输出空格,再退格就能删除前面的星号。例如我们输入了密码 123,它在屏幕上显示为:
输出一个退格,光标向前移动一个字符的位置,变为:
此时再输出一个空格,星号就会被覆盖掉,变为:
虽然星号被空格替换掉了,但是光标也同时向后移动了一个位置,这样光标和星号之间就有了一个字符的间隔,所以还需要再输出一个退格,消除间隔。再输出一个退格后变为:
函数体最后一行代码也至关重要,它向字符数组中添加了字符串结束符,决定者密码在何处结束。