http://c.biancheng.net/view/vip_1801.html

一个安全的程序在用户输入密码时不应该显示密码本身,而应该回显星号或者点号,例如······或**,这在网页、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;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    if(ch=='\b' && i>0){  //按下删除键
    i--;
    printf("\b \b");
    }else if(isprint(ch)){ //输入可打印字符
    pwd[i] = ch;
    printf("*");
    i++;
    }

}
pwd[i] = 0;
}
运行结果:
Input password: *
The password is: 123456789

代码中定义了一个函数 getpwd(),它有两个参数:pwd 为保存密码的内存的首地址,pwdlen 为密码的最大长度。

函数通过 while 循环来不断读取用户输入的字符,并逐一对它们进行处理:
如果用户按下回车键,表示输入结束了,getch() 将会读取到\r;
如果用户按下删除键,表示删除前面的字符,getch() 将会读取到\b;
如果用户输入可打印字符,那么就读取该字符并回显星号。

while 循环中之所以使用 getch() 来获取字符,是因为该函数既没有回显也没有缓存,可以立即读取到用户输入的字符,并且不会在屏幕上显示出来。

从循环条件 i<pwdlen 可以看出,当用户输入的密码超过最大长度时跳出循环,结束输入。
用户按下回车键时,getchar() 将读取到\n字符,而 getch() 将读取到\r字符。也就是说,对于不同的字符输入函数,回车键产生的字符不同,这个细节读者要引起注意。
删除密码的思路
密码保存在字符数组中,当用户按下删除键时,不仅要删除前面的星号,还应该删除字符数组中前面的元素。

需要注意的是,数组中的元素在内存中是连续分布的,无法直接删除。上面代码中,我们并没有对数组元素进行任何操作,而是将变量 i 减 1,跳过要删除的字符。这些本该被删除的字符依然留在数组中,它们会被后续输入的字符覆盖掉。

第29行代码中,我们通过printf(“\b \b”);来删除前面的星号。\b表示退格,也就是光标向后移动一个位置。退格,输出空格,再退格就能删除前面的星号。例如我们输入了密码 123,它在屏幕上显示为:
***_
输出一个退格,光标向前移动一个字符的位置,变为:
此时再输出一个空格,星号就会被覆盖掉,变为:
** _
虽然星号被空格替换掉了,但是光标也同时向后移动了一个位置,这样光标和星号之间就有了一个字符的间隔,所以还需要再输出一个退格,消除间隔。再输出一个退格后变为:
**_

函数体最后一行代码也至关重要,它向字符数组中添加了字符串结束符,决定者密码在何处结束。