文章程式碼顯示

2017年11月12日 星期日

《筆記》C語言 - 04:for 迴圈、switch 多重選擇敘述式、break 與 continue、ASCII 字元集


for 迴圈

  前一章我們說明了 while 迴圈的用法,我們提提另一種迴圈:for 迴圈 的用法。直接用一段程式碼來理解一下 for 迴圈

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int i;

 for( i=0; i < 10; i++){
  printf( "%d\n", i );
 }

 return 0;
}


程式中:

第 6 行:宣告一個整數型態的變數名稱為 i

第 8~10 行:第 8 行就是一個 for 迴圈的主要架構,在關鍵字 for 之後填上一個括號,括號內具有三個引數分別是 初始值、判斷式、遞增敘述彼此以分號隔開且遞增敘述後方不需要加上分號

當程式碼遇到 for 迴圈時,會先將變數初始化,在此例中也就會將變數 i 的值設為 0

接著進行判斷式(在此例中為 i < 10 ),當"條件成立"時會進行區塊內的敘述句(第9行)。

執行完區塊內的所有敘述句(又稱迴圈本體)後,才回頭執行"遞增敘述" (在此例中是 i++),然後"再次進行條件判斷",若判斷成立則進行區塊內的敘述句。

所以變數 i 會從一開始初始化的 0 ,經由不斷的條件判斷、執行區塊內敘述、遞增、條件判斷、執行區塊內敘述、遞增 ... 如此反覆執行。

當 i 為 9 時,因條件成立所以會執行第 9 行的程式碼,接著 i 遞增為 10 ,然後進行條件判斷發現不成立, 這才跳離開 for 迴圈。

所以,以 0 為初始值並以 10 為終止條件,整個迴圈將被執行 10次 。



上圖稱為程序流程圖,很明確的指出 for 迴圈初始值賦值的動作只會執行一次,以及遞增敘述發生在迴圈本體結束之後。

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int i;
 int answer;

 printf( "%10s%10s\n", "Calculate", "Answer" );

 for( i=1; i < 10 ; i++){
  printf( "     2 x %d", i );
  answer = 2*i;
  printf( "%10d\n", answer );
 }

}




程式中:

第 9 行:我們使用 printf 配合 %s 轉換指定詞來將 "字串" 引入,其中數字 10 表示要列印的欄位寬度(field width) ,若為正數則表示顯示幕上會先預留出 10 個字元的寬度且靠右對齊;若為負數則代表靠左對齊。

第 14 行:我們引入第 13 行的計算結果當作列印的參數。

在進行 for 迴圈本體撰寫時,可以用"代入法"在腦海中模擬一下我們想要呈現的效果為何,例如在上例中我們可以先假設 i =1 ,然後自己在腦中想像一下當 i 等於 1 時迴圈主體(11~14行)會怎樣執行,如此一來可以幫助我們撰寫 for 迴圈主體。

補充:
for ( expression1 ; expression2 ; expression3 ){
  statement
}

for 敘述句裡的三個運算式,事實上都是可有可無的。如果我們省略了 expression2 的話,則 C 語言會認為判斷條件永遠成立,因而建立一個無窮迴圈。如果控制變數已經在程式其它位置設定好了初始值,則我們可以省略 expression1 。如果遞增敘述在迴圈本體的敘述式當中執行,或是不需要遞增動作,則 expression3 便可省略。


switch 多重選擇敘述式

  前面我們談到 if ... else 選擇敘述式,假設我們現在有許多條件需要判斷,就會在程式碼中出現許多的 else if ,有時這對於版面的整潔度是具有不利影響的。這時我們可以嘗試使用 switch 來進行判斷。值得一提的是,switch 的判斷只適用在判斷"值(字元)"是否相等,並不能直接用在判斷值是否在某個區間;同時,字串也是不被允許的。

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int grade;

 while(1){

  printf( "Please enter your grade : " );
  scanf("%d",&grade);

  switch(grade){

   case 1:
    printf("Key 1\n");
    break;
   case 2:
    printf("Key 2\n");
    break;
   default:
    printf("Nothing\n");
    break;
  }
 }
 return 0;
}



程式中:

第 13~24 行:此部分就是  switch 多重選擇敘述式。一個 switch 敘述句可以對應到一連串的 case 標籤,在關鍵字 switch 後輸入變數 grade ,而區塊內就用 case 標籤加上一個空格再加上一個判斷值及冒號來做為不同條件判斷。

我們可以發現在每個 case 中都有加入 break 敘述句。 之前在 if ... else if ... else 選擇敘述句有提到 "當判斷條件成立並執行完該區塊的敘述句後,就會跳出 if ... else 架構而不會執行後續的判斷"。

而 switch 則要加入 break 語句才能達到同樣的效果,假設 case 1: 成立,執行第 16 行的敘述句,接著執行第 17 行的 break 敘述句,此時就會跳出 switch 架構。

default 敘述則代表著 case 標籤以外的條件。 default 不一定需要使用,若沒有指定 default 條件也不會出現編譯錯誤的狀況出現,但為了 switch 架構的完整性,建議還是要建立 default 較佳。

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int grade;

 while(1){

  printf( "Please enter your grade : " );
  scanf("%d",&grade);

  switch(grade){

   case 1:
    printf("Key 1\n");
   case 2:
    printf("Key 2\n");
   default:
    printf("Nothing\n");
  }
 }
 return 0;
}



倘若將 break 省略的話會將剩餘所有 case 中的敘述句都執行,輸出結果不如預期( break 的用法在本文後段 )。

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int grade;

 while(1){

  printf( "Please enter your grade : " );
  scanf("%d",&grade);

  switch(grade){

   case 1:
   case 3:
    printf("Key 1or3\n");
    break;
   case 2:
   case 4:
    printf("Key 2or4\n");
    break;
   default:
    printf("Nothing\n");
    break;
  }
 }
 return 0;
}


不同的 case 標籤還可以共用同一個敘述句

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int grade;

 while(1){

  printf( "Please enter your grade : " );
  scanf("%d",&grade);

  switch(grade){

   case 1:
   case 3:
    printf("Key 1\n");
    printf("Key or3\n");
    break;
   case 2:
   case 4:
    printf("Key 2\n");
    printf("Key or4\n");
    break;
   default:
    printf("Nothing\n");
    break;
  }
 }
 return 0;
}


敘述句的區塊,並不需要 { } 嚴謹的定義區塊。

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 char grade;

 while(1){

  scanf("%c",&grade);

  switch(grade){

   case 'a':
    printf("Key 1\n");
    break;
   case 'b':
    printf("Key 2\n");
    break;
   default:
    printf("Nothing\n");
    break;
  }
 }
 return 0;
}



在第 6 行中我們將變數 grade 宣告成 char 類型(字元類型),並且在第 10 行的地方使用 %c 來讀入"字元"。

由結果發現確實會顯示出 Key1 但奇怪的是每次都會連帶出現 Nothing 。其原因在於我們輸入字母 a 或 b 或 c 時,難免要敲擊鍵盤上的 Enter 按鍵,而這個 Enter 鍵也將被 scanf 函數所擷取,所以程式碼如果要正常的運作應該要增加 \n 的 case 來進行過濾

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 char grade;

 while(1){

  scanf("%c",&grade);

  switch(grade){

   case 'a':
    printf("Key 1\n");
    break;
   case 'b':
    printf("Key 2\n");
    break;
   case '\n':
    printf("");
    break;
   default:
    printf("Nothing\n");
    break;
  }
 }
 return 0;
}


改成這樣後,結果才符合我們的期待。

break 與 continue

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 int i;
 int j;

 for ( i=0; i < 10; i++){

  printf( "%d\n", i );
  if( i==5 ){
   break;
  }

 }

 printf("----------------\n");

 for ( j=0; j < 10; j++){

  if( j==5 ){
   continue;
  }
  printf( "%d\n", j );

 }

 return 0;
}


break 敘述句也可以用在 for 以及 while 迴圈。由上面的程式碼中可以看到,當 i 等於 5 時執行 break 敘述句 " 跳出現有迴圈架構 "

當continue 敘述句執行時會直接跳回迴圈判斷式的地方(也就是第20行)。以上方程式碼舉例,當 j 等於 5 時將執行 continue 敘述句,就會因此跳過第 25 行的 printf 語句。



當我們使用 switch 敘述式時,要記得它只能檢驗 "常數整數"是否相等

常數整數包含「字元常數」和「整數常數」。

字元常數的表示方式是將字元放在單引號中,如 'a' 。字元必須放在單引號中,才會被視為是字元常數。

放在雙引號中的字元會被認為是字串。而整數常數則指的是一般的整數值。

能夠辨識整數常數(整數值)還很好理解,但為什麼還可以辨識字元常數呢? 事實上字元常數被C語言 認為是一個整數值。這與 ASCII 字元集有關

 ASCII 字元集

在 C 語言中,字元通常是存到 char 型別的變數中,但字元還可以存成任何的 "整數資料型別" ,也就是說在電腦裡,字元可以表示成一個 byte 長度( 8 bit )的整數。因此我們可視需要將字元當成一個整數或是字元,例如

#include "stdio.h"

int main(void) {
setvbuf(stdout,NULL,_IONBF,0);

 printf("The character (%c) has the value %d.\n", 'a', 'a');

 return 0;
}


%c 是引入字元常數的轉換指定詞(就像 %d 是引入整數的一樣),我們在第二個引數的地方指定為 %d ,並且將 'a' 輸入進去發現顯示出來的結果是 97 。也就是說對於電腦而言, 「a 可以視為字元 'a' 或是整數 97」

整數 97 是字元 a 在電腦中的數字表示方式。目前大多數的電腦都使用 ASCII 字元集,而在此字元集中就定義了小寫 a 為 97 (Dec十進制)



↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引