C 語言初學教材 - 第五章 使用全域變數的帳號管理程式

現在我們來把帳號管理程式模組化,這個版本將採全域變數的設計方式,也就是說,會把兩個二維字元陣列宣告在所有函數的定義之外

// 在所有函數外宣告預設的帳號及密碼
char userID[SIZE][LEN];
char userCODE[SIZE][LEN];


我們會定義如下各個函數
// 函數原型的宣告
int manage(int counter);
void printList(void);
void si(void);
int ssearch(char array[][LEN], int size, char *target);
int di(int counter);
int so(void);
void ssort(char array[][LEN], char code[][LEN], int size);


函數 main() 處理最外層的登入,而 manage() 處理帳號管理的部份,這是說一旦比對使用者輸入的帳號為 administrator ,密碼也與預設的 0000 相符, main() 便呼叫 manage() 進行帳號管理工作
if (i == 0 && !strcmp(inputCode, userCODE[i])) {
    counter = manage(counter);
    printf("\n\n");
    state = RUN;
    break;
}


簡單說,由於陣列的索引 counter 有可能被管理者更改,例如刪除或排序,所以 manage() 需要 counter 當參數,也回傳整數型態的 counter 。定義如下
int manage(int counter)
{
    // 宣告接收管理模式指令
    char instruction[LEN];
    
    printf("\n\n您成功以管理員模式登入....\n");
    printf("將入帳號管理模式,請在提示符號 # 後輸入指令\n");
                    
    while (1) {
        printf("\n# ");
        scanf("%s", instruction);
                        
        // exit 為離開管理模式的指令
        if (!strcmp(instruction, "exit")) {
            return counter;
        }
                        
        // list 為列印使用者列表的指令
        if (!strcmp(instruction, "list")) {
            printList();
            continue;
        }
                        
        // search 為查詢帳號指令
        if (!strcmp(instruction, "search")) {
            si();
            continue;
        }
                        
        // delete 為刪除帳號指令
        if (!strcmp(instruction, "delete")) {
            counter = di(counter);
            continue;
        }
                        
        // sort 為排序指令
        if (!strcmp(instruction, "sort")) {
            counter = so();
            continue;
        }
    }               
}


list 、 search 、 delete 、 sort 各項指令會呼叫各自相關的函數,而 exit 會使 manage() 回傳 counter 值。函數 manage() 就是在一個 while (1) 迴圈下執行,當管理者輸入 exit 指令後,才會執行 return 陳述,將 counter 回傳給函數 main() ,這是 manage() 的結束機制。


這裡我們也提醒一點,函數的 return 陳述不一定要放在函數定義的最後面,如此例或之前版本的 break 陳述,適當的設計可以避免程式進行過多不必要的條件檢查。


函數 printList() 用為印出所有使用者帳號,定義如下
void printList(void)
{
    int i;
    
    printf("\n以下為所有註冊使用者的帳號及密碼\n");
    printf("\n帳號 - 密碼\n");
    
    for (i = 0; i < SIZE; i++) {
        if (userID[i][0] == '\0') {
            continue;
        }
                                
        printf("%s - %s\n", userID[i], userCODE[i]);
    }
}


由於 userID[][] 及 userCODE[][] 都是全域變數,因此在 printList() 可以直接使用。


函數 si() 為查詢使用者帳號與管理者互動的介面,實際搜尋演算法寫在函數 ssearch() 之中。
void si(void)
{
    char searchname[LEN];
    int index;
                            
    printf("\n請輸入要查找的帳號: ");
    scanf("%s", searchname);
    index = ssearch(userID, SIZE, searchname);                        
                            
    if (index != -1) {
        printf("\n帳號 %s 已經註冊,密碼是 %s\n", userID[index], userCODE[index]);
    }
    else {
        printf("\n還沒有 %s 的帳號註冊唷!\n", searchname);
    }
}

int ssearch(char array[][LEN], int size, char *target)
{
    int i;
    
    for (i = 0; i < size; i++) {
        if (!strcmp(array[i], target)) {
            return i;
        }
    }
    
    return -1;
}


這裡須留意, si() 不需要參數,這是因為 si() 可以直接存取全域變數的 userID[][] 及 userCODE[][] ,但是 ssearch() 仍需要原先定義的陣列名稱、大小及搜尋目標當作參數,這是因為我們設計出的 ssearch() 可搜尋任何二維字元陣列。


另外跟原先把程式都擠在函數 main() 之中也有不同的設計機制,我們本來是用狀態變數 Y 及 N 來記錄是否搜尋成功,現在直接由 ssearch() 的回傳值來處理。搜尋失敗,也就是沒有找到目標值, ssearch() 回傳 -1 ,這是說當 index 不等於 -1 之時,搜尋就是成功,反之搜尋失敗。


函數 di() 為刪除帳號指令的與管理者互動介面,定義如下
int di(int counter)
{
    char deletename[LEN];
    int index;
                            
    printf("\n請輸入要刪除的帳號: ");
    scanf("%s", deletename);
    index = ssearch(userID, SIZE, deletename);
                            
    if (index != -1) {
        for (index; index < SIZE; index++) {
            strcpy(userID[index], userID[index + 1]);
            strcpy(userCODE[index], userCODE[index + 1]);
        }
                                
        printf("\n%s 的帳號資料已刪除\n", deletename);
        return counter--;
    }
    else {
        printf("\n%s 的帳號資料不存在,無法刪除\n", deletename);
        return counter;
    }
}


di() 同樣以 ssearch() 為核心的搜尋演算法,若搜尋成功便可取得所要刪除陣列元素的索引值,然後才進行刪除的工作。


函數 so() 為排序指令的與管理者互動介面,而 ssort() 為實際核心的排序演算法,定義如下
int so(void)
{
    ssort(userID, userCODE, SIZE);
    printf("\n資料已排序完成\n");
    return 1;
}

void ssort(char array[][LEN], char code[][LEN], int size)
{
    int i, j;
    char tempa[LEN], tempb[LEN];
    
    for (i = 0; i < size - 1; i++) {
        for (j = 1; j < size; j++) {
            if (array[j - 1][0] > array[j][0]) {
                strcpy(tempa, array[j - 1]);
                strcpy(tempb, code[j - 1]);
                strcpy(array[j - 1], array[j]);
                strcpy(code[j - 1], code[j]);
                strcpy(array[j], tempa);
                strcpy(code[j], tempb);
            }
        }
    }
}


so() 最後回傳 1 這是因為排序完之後,我們把 counter 重新設為 1 。這裡我們同樣沿用已經開發好的 ssort() ,但是多增加一個二維字元陣列的參數,因為需要同時搬移兩個二維字元陣列的資料。


完整的範例程式碼及編譯執行,請繼續參考




沒有留言: