switch

我們知道,流程控制結構可分三類:迭代、邏輯分岔、遞迴。 現在我們已經知道 C 語言的所有迭代結構:whiledo-whilefor。 以前,在邏輯分岔這一類,我們只知道 if-else 結構。 現在介紹第二種:switch。其實,也就只有這兩類。

switch 的基本結構如下。

    switch (EXPR) {
    case CONST_A:
        BODY_A
    case CONST_B:
        BODY_B
    default:
        BODY
    }
其中 EXPR 是一個 C 的語句,任何語句都可以, 只要它產生的值是整數類的資料型態。 而 CONST_ACONST_B 必需是不同的常數整數、 或常數整數所組成的語句,例如 3 或 '\n'3+5 之類的。 如果有必要,可以增加更多的 casecase 語句以冒號 : 結尾。

EXPR 產生的值 == CONST_A 的時候,就會執行 BODY_A 裡面的指令。按照正常的流程來執行。 但是,要非常非常注意,switch 在此時並不像是 if-else 結構。 當 BODY_A 裡面根本沒有指令的時候,不必寫一個單獨的分號 ;。 而當 BODY_A 裡面的指令都做完了之後,流程不會跳到 switch 結束的地方, 而是繼續執行 BODY_B 裡面的指令、 甚至於繼續執行 BODY 裡面的指令。 一直到 switch 結構的結束為止 (到了 } 處)。 如果要 C 只執行 BODY_ABODY_B, 必須自己寫上一個 break; 指令。 它代表結束最內層的 forwhiledo-whileswitch 結構。

舉例來說,以下是一個 switch 結構的正確用法:(雖然有些無聊)

    switch (c = getchar()) {
    case ' ':
    case '\t':
        printf("c is a white\n");
        break;
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        printf("c is a digit\n");
        break;
    default:
        printf("c is something else\n");
        break;
    }
理論上,default: 是寫在最後面的,所以,它的 BODY 部分, 應該可以不寫 break;。 這個推理是對的。但是,最好養成習慣,還是寫上。 以免以後修改程式的時候,將 default: 改成另一個 case, 另外加一個 default:,然後就忘了要添 break; 在新的 case 裡面。

現在,我們要寫一個比較有用的範例程式 par2str.c。 這個程式,將輸入文字檔的段落當中的 LF 都換成 \n" 都換成 \"\ 都換成 \\,並且在所有輸出字元的最前面和最後面, 添加 "。 所謂段落,就是以一個以上的空列所隔開的字串。 但是僅含有空白的列,並不算是空列。 換句話說,par2str.c 將每個段落變成一個字串。


#include <stdio.h>
#define BUFSIZE 81
#define WHITE(c) (c == ' ' || c == '\t')
#define IN 1
#define OUT 0
#define LF '\n'
#define QUOT '\"'
#define NUL 0
    
int getline(char[], int);
    
/* 將文字檔案的每一個段落,變成一個字串  (par2str.c) */
main() {
    int i, par=OUT, line=OUT;
    char buf[BUFSIZE];
    
    while (getline(buf, BUFSIZE) > 0) {
        if (buf[0] == LF && par == IN && line == OUT) {    /* par ends */
            putchar(QUOT);
            putchar(LF);
            par = OUT;
        }
        if (buf[0] != LF && par == OUT) {                  /* par begins */
            putchar(QUOT);
            par = IN;
        }
        if (par == IN) {
            for (i=0; buf[i] != NUL; ++i)
                switch (buf[i]) {
                case LF:
                    printf("\\n");
                    break;
                case QUOT:
                    printf("\\\"");
                    break;
                case '\\':
                    printf("\\\\");
                    break;
                default:
                    putchar(buf[i]);
                    break;
                }
            if (buf[i-1] == LF)
                line = OUT;
            else
                line = IN;
        }
    }
    if (par == IN)
        printf("\"\n");
}

par 這個旗標,用來決定是否在一個段落的裡面。 利用這個旗標,我們避免被連續的空列所誤導。 而 line 這個旗標,用來決定是否在一列文字裡面。 利用這個旗標,我們避免因為太長的一列所產生的錯誤。 什麼錯誤呢?其實只有一種極端狀況。 那就是,如果某一列恰好在第 BUFSIZE 個字元出現了 LF, 那麼,這個 LF 就不會在第一回被 getline() 讀進去。 但是當第二回被 getline() 讀到 buf[] 裡面去的時候, buf[0] 恰好就是 LF。 這樣,會被誤判為段落結束了。

例如,檔案 aa 的內容是

a b
   c
   
d

e
則被 par2str 過濾之後,就會變成
"a b\n   c\n   \nd\n"
"e\n"
我們看到,cd 之間並非空列,而是含有 3 個空格。

習題

  1. 寫一個程式,取名叫 str2par.c,它將輸入的純文字檔案內的一個字串, 輸出成一個段落。段落與段落之間用一個空列隔開。 所謂字串,就是一對 " 之間的字元 (不含前後的 " 本身)。 並且,str2par 把字串內的 \n 都換成 LF, \" 都換成 "\\ 都換成 \,其他字元不變。

[ 前一節 ]‧[ 後一節 ]‧[ 回目錄 ]



注意:此處所有文件均為原著,個別的版權宣告日後會一一公布, 整體版面設計亦尚未完成。但仍請勿抄襲文字與圖片,以免觸犯著作權法。

Created: May 20, 2000
Last Revised: May 21, 2000
© Copyright 2000 Wei-Chang Shann 單維彰

shann@math.ncu.edu.tw