利用堆疊 (Stack) 的後進先出 (LIFO) 特性,實現一個支持退格刪除、掩碼顯示 (星號)、限制嘗試次數 的控制台密碼驗證程序。代碼包含完整的堆疊 ADT 實現 (動態擴容)、密碼輸入交互邏輯、驗證核對流程,適合作為數據結構課程實驗或嵌入式終端項目參考。
#define STACK_INIT_SIZE 10
#define OK 1
#define TRUE 1
#define FALSE 0
#define ERROR 0
typedef char SElemType;
typedef struct STACK {
SElemType *base; // 動態數組基址
SElemType *top; // 棧頂指針 (指向下一可用位置)
int stacksize; // 當前分配容量
int length; // 當前元素個數
} SqStack, *Stack;
typedef int Status;
void InitStack(Stack *S) {
*S = (SqStack*)malloc(sizeof(SqStack));
(*S)->base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!(*S)->base) exit(-1);
(*S)->top = (*S)->base;
(*S)->stacksize = STACK_INIT_SIZE;
(*S)->length = 0;
}
Status DestroyStack(Stack *S) {
free((*S)->base);
free(*S);
return OK;
}
void ClearStack(Stack *S) {
(*S)->top = (*S)->base;
(*S)->length = 0;
}
Status StackEmpty(SqStack S) {
return (S.top == S.base) ? TRUE : FALSE;
}
Status GetTop(Stack S, SElemType *e) {
if (S->top == S->base) return ERROR;
*e = *(S->top - 1); // 核心:top指向下一位置,頂元素在top-1
return OK;
}
void Push(Stack *S, SElemType e) {
// 滿棧擴容:每次增加2個單元
if ((*S)->top - (*S)->base >= (*S)->stacksize) {
(*S)->base = (SElemType*)realloc((*S)->base,
((*S)->stacksize + 2) * sizeof(SElemType));
if (!(*S)->base) exit(-1);
(*S)->top = (*S)->base + (*S)->stacksize;
(*S)->stacksize += 2;
}
*((*S)->top++) = e;
++(*S)->length;
}
Status Pop(Stack *S) {
if ((*S)->top == (*S)->base) return ERROR;
(*S)->top--;
--(*S)->length;
return OK;
}
void Change(SqStack S, char *a) {
int n = S.length - 1;
while (!StackEmpty(S)) {
GetTop(&S, &a[n--]); // 從棧頂彈出,填入數組尾部 → 正序
}
}
Control()
void Control(Stack *s) {
int i = 0, k, j = 0;
SElemType ch, *a;
k = strlen(PASSWORD); // 全局密碼 "abcdef"
printf("輸入一個 %d 位密碼,你只有 3 次機會:\n", k);
printf("C:\\>");
for (;;) {
// 超過3次失敗 → 退出
if (i >= 3) {
i++; clrscr(); gotoxy(1,1); break;
}
// 第2、3次嘗試:清屏重置顯示
else if (i > 0 && i < 3) {
gotoxy(5,2);
for (j = 1; j <= (*s)->length; j++) printf(" ");
gotoxy(5,2); ClearStack(s);
}
// 密碼輸入循環 (支持退格)
for (;;) {
ch = getch(); // 無回顯讀取
if (ch != 13) { // 非回車
if (ch == 8) { // 退格鍵 ASCII 8
Pop(s);
gotoxy(4 + j, 2); printf(" "); gotoxy(4 + j, 2); // 擦除星號
} else {
printf("*"); // 掩碼顯示
Push(s, ch);
}
j = (*s)->length;
} else break; // 回車結束輸入
}
i++; // 嘗試次數+1
// 長度不匹配直接繼續下一次
if (k != j) continue;
// 逐字符比對 (Change已將棧內容按正序存入a)
a = (SElemType*)malloc((*s)->length * sizeof(SElemType));
Change(*s, a);
for (j = 1; j <= (*s)->length; ) {
if (a[j-1] == PASSWORD[j-1]) j++;
else { j = (*s)->length + 2; break; }
}
if (j == (*s)->length + 2) continue; // 不匹配
else break; // 匹配成功
}
if (i == 4) printf("\n密碼錯誤,即將退出");
else printf("\n密碼正確\n");
free(a);
}
char PASSWORD[10] = "abcdef"; // 全局預設密碼
int main() {
Stack s;
clrscr(); // 清屏 (conio.h)
InitStack(&s);
Control(&s);
getch(); // 暫停查看結果
DestroyStack(&s);
return 0;
}
conio.h 的 DOS/Windows 編譯器stdio.h, string.h, conio.h, stdlib.hclrscr() 清屏、gotoxy(x,y) 光標定位、getch() 無回顯讀鍵| 亮點 | 說明 |
|---|---|
| 動態擴容堆疊 | 避免固定大小限制,realloc策略簡單有效 |
| 退格可視化 | 配合gotoxy實現星號擦除,用戶體驗佳 |
| 嘗試次數限制 | 防暴力破解基礎機制 |
| 堆疊反序導出驗證 | 巧妙利用LIFO特性將輸入順序還原 |
conio.h 依賴:使用跨平台終端庫 (ncurses / Windows Console API) 或標準輸入非規範模式 (termios / SetConsoleMode)。malloc/realloc 失敗時優雅退出而非 exit(-1)。stack.h/.c,密碼邏輯獨立 auth.h/.c。本程序以堆疊為核心數據結構,完整演示了從底層 ADT 實現到上層交互邏輯的構建過程。代碼雖基於經典 Turbo C 環境,但其算法邏輯 (動態棧、退格處理、掩碼、限嘗試) 具備普遍參考價值。理解並移植它,將有助於掌握「數據結構服務於業務邏輯」的工程思維。