發信人: ax.bbs@bbs.ee.nthu.edu.tw. (athena), 信區: test
標  題: 星星流講座 0020
發信站: ☆清華電機☆ (Tue May 23 11:20:45 1995)
轉信站: star

星星流講座 0020         C 語言教室

第 4 講 之 5            基本的流程控制
                        Topic: for loop (1)

我們前面討論了 while 和 do-while 的迴圈,為什麼叫迴圈呢?因為在條件
成立的時候會一直執行重複的程式區塊,所以我們叫它迴路或迴圈。許多迴
圈在執行的時候是以執行的次數做為條件的,比方說:

        i = 0;
        while (i < 10)
        {
            i++;
            /* 迴圈的剩餘部分,但是不會用到 i,i 只是個計次用的變數 */
        }

這個時候我們就可以利用 for 迴圈來簡化上面的程式:

        for (i = 0; i < 10; i++)
        {
            /* 迴圈 */
        }

for 的用法是這樣的:

                for (i = 0; i < 10; i++)
                     ↗       ↑       ↖   
             計次用變數的   迴圈執行    計次用變數
                初始值      條件        每次值的變化

在做進一步的深入探討之前,請你寫作一個九九乘法表的程式,它的輸出和小
學生墊板後面的相同。

for 迴圈最有意思的是計次變數初始值、迴圈執行條件及計次變數的變化都可以不
給,例如:

        for ( ; i > 10; i--)    /* 前面已給過 i 的初值了,所以迴圈不必再給 */
        for ( ; i > 10; )       /* 迴圈中會不規則的變動 i,把 for 當成 while */
                                /*   來用,這是因為有人不會用 while :) */
        for ( ; ; )             /* 永不停止的迴圈 (無窮迴圈 infinite loop) */

以上是最常見的三種情形,其他的情形除非你有特別巧妙的設計,否則該用 while
的時候還是用 while 來做比較清晰易懂。

/* shellsort.c */
#include <stdio.h>

void shellsort (int v[], int n)         /* v[]: array to sort */
{                                       /* n: # elements in array */
    int gap, i, j, temp;

    for (gap = n / 2; gap > 0; gap /= 2)
    {
        printf ("shell sort gap %d\n", gap);
        for (i = gap; i < n; i++)
        {
            printf ("shell sort level %d\n", i);
            for (j = i - gap; j >= 0 && v[j] > v[j + gap]; j -= gap)
            {
                temp = v[j];
                v[j] = v[j + gap];
                v[j + gap] = temp;
            }
        }
    }
}

上面的程式是欣賞用的,我們的目的並不在講 shell sort,而是要和各位談談如何
寫出「乾淨」的程式碼。首先請看到上面程式裡的大括號都是上下對齊的,大括號
內的東西向內縮四格,為什麼要這樣做呢?看看以下錯誤的示範你就知道了:

        for (i = 0; i < 10; i++) {
                /* .... */
        }

第一個不好的習慣是上下的大括號不對齊,像 vi 或 jove 這種編輯器會自動替你
match 括號,提醒你是否有括號不對稱的情形,這當然是很好用的工具。但是如果
我們現在用的是像 PE2  這種不會替你對括號的編輯器,而你自己又不把括號對齊
的話,那麼可以預見的是你常常會得到這個錯誤訊息:

        parse error at end of input

第二個不好的習慣是程式碼不縮排,例如:

    for(gap=n/2;gap>0;gap/=2)
    {
    printf("shell sort gap %d\n",gap);
    for(i=gap;i<n;i++)
    {
    printf("shell sort level %d\n",i);
    for(j=i-gap;j>=0 && v[j]>v[j+gap];j-=gap)
    {
    temp=v[j];
    v[j]=v[j+gap];
    v[j+gap]=temp;
    }
    }
    }

所有的碼擠成一堆,一點層次感也沒有,層次感除了美觀之外,它其實有重要的功
用,在像上面的多重迴圈裡我們要很用力的看才能看出它在幹什麼,而不能如前面
的 shellsort.c 那麼一目了然。程式碼易讀的最大好處是縮短除錯的時間,減少
維護程式的成本


至於運算元 (operator) 前後的空白,例如 i = j + 4; 等等,筆者個人是模仿
Quick BASIC 的風格,如果是單元運算元 (unitary operator) 就不加空白,如:
i++; ,二元運算元 (binary operator) 則前後加空白,如:i = j;。左小括號 (
前面一定加空白,逗號 , 後面也一定加空白。在 C 語言聖經 K&R 的版本裡他們分
的更細,函數的左小括號前頭沒有空白,非函數的左小括號前頭有空白,例如:

        printf("Hello, world!\n");      /* 函數 */
        for (i = 1; i < 10; i++)

筆者因為很喜歡按大大的 space 鍵聽它叩叩的聲音,所以左括號前一律加空白 :)
初學者常常因為懶惰而吃掉不少空白,套句補習班常用的廣告詞:今日不做,明日
後悔。別因一時的懶惰種下明日的苦果,什麼苦果?等你程式寫到上千行你就知道了。