do-while

在這一節裡面,我們要介紹一個新的流程控制結構:do-while。 它的基本結構是

    do
        BODY
    while (CONDITION);
其中 BODY 是一些指令。如果超過一條指令,要用一對 { } 將它們括起來。 如果沒有指令,必須寫一個 ; 分號。 C 會先執行 BODY 一遍,然後檢查 CONDITION 是否為 True (只要 CONDITION 的值不是 0,就認為是 True)。 如果 True,則回頭重複執行 BODY,否則就做 while (CONDITION); 的下一條指令。

相信讀者在此時已經對流程控制結構,如 for, while, if-else,有了概念。 相對於 while 迴圈,do-while 迴圈的不同是, 在 while 裡面,先檢查 CONDITION,再決定 BODY 做還是不做。 在 do-while 裡面,先做一遍 BODY,在檢查 CONDITION,再決定繼續還是停止。 所以,在 while 裡面,BODY 可能一次都沒執行。 在 do-while 裡面,BODY 至少被執行了一遍。

以下舉一個函式 i2s() 來作為 do-while 結構的使用範例。 在 printf() 的輸出格式中,我們可以用十進制、八進制、十六進制數字, 來輸出一個整數。但是卻沒有輸出二進制數字的格式。 以下的 i2s(),就是要彌補這個缺憾。


/* 將一個正整數轉換成二進制數字的字串 */
void i2s(unsigned int n, char s[]) {
    int i=0, j, c;
    do
        s[i++] = (n & 1) + '0';
    while ((n >>= 1) > 0);
    s[i] = '\0';
    for (j=0, --i; j < i; c=s[j], s[j++]=s[i], s[i--]=c) ;
}

注意,此函式並不理會正負號的問題。因為,通常當我們要輸出二進制數字的時候, 都是為了要看到資料的位元組,所以,考慮負數是沒有意義的。 而且,原函式要負責讓 s[] 的維度夠長。

讓我們看看 i2s() 的設計。 其中 (n & 1) 就是檢查 n 的位元組之 0 號位元是否為 1。 如果不是 1 (那就是 0),就在 s[] 裡面添一個 '0' 數目字。 如果是 1,就要添一個 '1' 數目字,而 '1' 數目字的 ASCII 號碼,就是 '0' 數目字的下一號。 所以我們寫

        s[i++] = (n & 1) + '0';
而這就是 do-while 的 BODY 部分。 此時,我們之所以選擇用 do-while 而不用 while, 原因非常明顯:如果輸入的 n 就是 0, 則 s[] 至少要放一個 '0' 進去。 所以,此 do-while 的 BODY 部分至少要被執行一遍。如果不慎寫成了
    while ((n >>= 1) > 0)
        s[i++] = (n & 1) + '0';
則遇到 n 本來就是 0 的 極端狀況,就會出錯 (s[] 成了空字串)。 i2s() 的最後一個指令,其實就是要將 s[] 的字元順序反過來。 我們可以呼叫 reverse() 來執行。 但是,為了這麼簡單的工作,還要宣告、呼叫另一個函式, 我覺得太麻煩了,所以就將 reverse() 的功能寫在 i2s() 裡面。

底下,我們寫一個簡單的程式來測試 i2s()


#include <stdio.h>

void i2s(unsigned int, char[]);

/* Test for i2s()  (test_i2s.c) */
main() {
    char s[8*sizeof(unsigned int)+1];
    int x;

    x = 13, i2s(x, s);
    printf("%d\t%s\n", x, s);
    x = 0, i2s(x, s);
    printf("%d\t%s\n", x, s);
    x = (1 << 31), i2s(x, s);
    printf("%d\t%s\n", x, s);
    x = -1, i2s(x, s);
    printf("%d\t%s\n", x, s);
}

雖然我們知道,在大部分機器上,unsigned int 都是 32 位元長的資料型態。 但是,此刻我們這個程式可以更安全地從一台機器換到另一台機器去執行。 即使,在某台機器上,unsigned int 的位元數不是 32,也不會出錯。 我們宣告 s[] 的維度是 8*sizeof(unsigned int)+1, 那是為了留一個位置擺零字元 ('\0')。

假設 i2s() 寫在 i2s.c 裡面, 測試程式寫在 test_i2s.c 裡面,則執行步驟如下:

shell% gcc -c i2s.c
shell% gcc test_i2s.c i2s.o
shell% a.out
13      1101
0       0
-2147483648     10000000000000000000000000000000
-1      11111111111111111111111111111111
我們看到,因為 C 自動處理 int 和 unsigned int 資料型態的特性, 所以即使在 main() 中將 x 定義成負數, 還是可以得到正確答案。

習題

  1. 寫一個符合以下規格的函式
    void i2sm(unsigned int n, char s[], unsigned int m)
    
    它將 n 的數值轉換成二進制的數字,儲存在 s[] 裡面。 但是 s[] 至少要用掉 m 個格子。 如果 s[] 裡面的字元超過了 m 個, 就讓它超過。如果不足,必須在左邊補空格,使它總共有 m 個字元。 譬如,呼叫 i2sm(13, s, 8),則 s 字串就應該是 " 1101"
  2. 寫一個程式,從 stdin 讀入一個純文字檔, 假設檔案內每列寫了一個以十進制數字表達的整數。 按照原來檔案的順序,將它們逐一轉換成二進制數字輸出。
  3. 寫一個程式,從 stdin 讀入一個純文字檔, 假設檔案內每列寫了一個以十進制數字表達的正整數, 而且每個數小於 232。 輸出這一系列正整數的 位元和 (bit-sum)。 也就是說,按照位元的位置來做加法所得的和。 例如輸入的是
    1
    3
    6
    7
    
    則它們的位元組是
    0001
    0011
    0110
    0111
    
    按照位元的位置做加法,就相當於
        0   0   0   1
        0   0   1   1
        0   1   1   0
     +  0   1   1   1
     ----------------
        0   2   3   3
    
    所以位元和就是
    0 2 3 3
    
    一般而言,您要輸出 8*sizeof(unsigned int) 個數來表達位元和。

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



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

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

shann@math.ncu.edu.tw