充份使用 CPU 的 for 迴圈
參考:
說明:
電腦最厲害的地方在於它的速度,
CPU 的時脈愈來愈快,
(由 1985 年的 8 MHz, 16, 33,
50, 66, 100, 120, 133, 166, 200, 233, 266, 300, 400, 448MHz,
566, 800, 933, 1GHz, 1.5G, 2G, 2.4G, 2.66G, 2.8G, 3.0G, 3.4G,
15 年間 CPU 時脈加快了 300 倍以上
(CPU 的速率不只看時脈就是了、也看它的架構,比方說
CoreIIDuo 比 P4 快,P4 就比 P3 快...
1GHz 的意思大概是說如果你只用 CPU 做加法,
每秒鐘大約可以做 10^9 次,
這是相當快的速度,
如果能夠好好運用,
自然可以替我們做好多的事。
現在有一個問題:
如果程式中每一個指令 CPU 都只做一次,
那麼要讓 CPU 執行 1 秒就要寫 10^9 個指令,
這麼大的程式有可能嗎?
我們現在要介紹的 for 迴圈最主要的目的就是要讓 CPU 重覆做某些指令,
讓我們可以用很少的指令來控制 CPU 做很多事情,
以便充分地利用 CPU 的能力,
此外此種敘述可以很簡單地控制究竟要重覆幾次。
例如: 重覆加法 10000 次
int i;
int sum=0;
for (i=0; i<1000; i++)
{
sum = sum + 1;
}
或是
int i, sum;
for (i=0,sum=0 ;i<10000; i++)
sum++;
上例中 i 我們稱為迴圈 index,
i=0 設定 i 的初始值為 0,
i<10000 為結束測試值,
i=i+1 (或用 i++) 為 index 增加其數值,
每做一次迴圈後會執行此敘述將 index 變數的內容增加 1。
此程式片段之流程圖如下:
注意:
上面這個範例當然也可以寫成
int i, sum;
for (i=1, sum=0; i<=10000; i=i+1)
sum = sum + 1;
但是在撰寫 C 程式時大部份的人都習慣使用前者,
最主要的原因是因為要配合陣列的存取:
在 C 語言中陣列的 index 是由 0 開始計算的。
測試範例:
下面的幾個範例在文法上都是被允許的,
請特別注意迴圈結束之測試:
-
int i;
for (i=0; i<10; i=i+1)
{
..........
}
-
int i;
for (i=1; i<=10; i=i+1)
{
..........
}
-
int i;
for (i=10; i>0; i=i-1)
{
..........
}
-
int i;
for (i=10; i>=1; i=i-1)
{
..........
}
-
int i;
for (i=1; i!=10; i=i+1)
{
..........
}
-
int i = 0;
for(; i<10;)
{
.........
i = i + 1;
}
-
float x;
for (x=1.57; x<=10.0; x=x+3.14159)
{
..........
}
-
float x;
for (x=0.0; x!=3.3; x=x+0.3)
{
..........
}
-
float x;
for (x=100; x>-20.57; x=x-11.11)
{
..........
}
上面的這些範例中, 除了
float x;
for (x=0.0; x!=3.3; x=x+0.3)
{
..........
}
是被文法允許但是實際上是有經驗的程式設計者避免使用的,
其他的各個範例你都可以放心使用,
禁止使用的原因是像這樣子的迴圈很可能會成為一個無窮迴圈而無法結束:
理論上這個迴圈應該可以執行 12 次然後結束,
但是由於浮點的表示法會有 round off 誤差,
也就是說,
在 CPU 內部,
0.0 加上 0.3 十二次之後不見得會等於 3.3,
誤差也許只有 10^(-15) 這麼大而已,
但是結果終究可能會不相等,
因此如果我們要確保此迴圈會正常地如我們所預期般結束的話,
使用 x<=3.3 才對,
此時這個迴圈可能會在執行 11 次或 12 次後停止,
端視 0.0 加上 3.3 十二次以後是稍大於 3.3 還是稍小於 3.3 而定,
但是這樣子仍然不夠好,
一個不知道執行 11 次還是 12 次後才會停下來的迴圈你會想要用嗎?
比較好的解決方法就是使用整數來當 index
而不是用會產生微小誤差的浮點數來當 index。
由於上述原因, 在一般的情形下我們也儘量避免
int i;
for (i=1; i!=10; i=i+1)
{
..........
}
這樣子的用法,
以免徒增困擾,
雖然此時並沒有錯誤,
但是這種格式及思維方式很容易出現錯誤,例如:
int i;
for (i=1; i!=10; i=i+2)
{
..........
}
就是一個無窮迴圈。
也許你會說:不會啦!
這種情況一看就知道有錯啊,
怎麼會用呢?
再看看下面的範例你就知道不一定看得出來喔!
int i;
int start;
start = someFunction();
for (i=start; i!=10; i=i+2)
{
..........
}
注意:
其實迴圈
float x;
for (x=0.0; x<3.0; x=x+0.3)
{
..........
}
我們知道它會執行 11 次,
有一點驚訝嗎?
原因請你去看看二進位的小數表示法吧!
相似的例子還有:
float x;
for (x=0.0; x>-1.0; x-=0.1)
printf("Hello\n");
到底會印幾次呢?
float x;
for (x=0.0; x<10.0; x+=1.0)
printf("Hello\n");
到底會印幾次呢?
float x;
for (x=0.0; x<1.1; x+=.1)
printf("Hello\n");
到底會印幾次呢?
float x;
for (x=0.0; x<0.1; x+=.1)
printf("Hello\n");
到底會印幾次呢?
int x;
for (x=0.0; x<1.0; x+=.1)
printf("Hello\n");
到底會印幾次呢?