零基礎學C語言——循環與控制結構

這是一個C語言系列文章,如果是初學者的話,建議先行閱讀之前的文章。筆者也會按照章節順序發佈。

本篇詳細講解循環結構與控制結構。對於每一種編程語言來說,這都是必不可少的結構。

循環結構

循環結構是為了簡化重複工作而給出一種語句

前面 一文中曾提及,函數是對一種功能的封裝,以便日後直接使用這種功能。看似都是簡化重複工作,但它們之間並非替代關係。

例如有一個需求:向文件中寫入一千萬行相同數據。

未學習循環時,只有將寫數據函數在代碼文件中寫一千萬行來完成。

這樣的代碼...

零基礎學C語言——循環與控制結構

編輯代碼極慢,且生成的二進制文件相較於同功能使用循環的代碼來說要大很多(一千萬個call指令)。

然而利用循環,上述的需求三行代碼即可搞定。

來簡單看看循環結構的執行流程

零基礎學C語言——循環與控制結構

循環結構流程圖

通常,程序執行到循環結構時會先判斷循環條件是否滿足,滿足循環條件,則進入循環內部執行相應語句(上例的調用寫文件函數),然後繼續判斷循環條件(是否不足一千萬次)是否滿足,如此往復,直至循環條件不滿足時,跳出循環。

在C語言中有三種循環結構:

  • while
  • do-while
  • for

while

while語句的一般形式如下:

<code>while (循環條件) {  ...//循環內的語句}//如果循環內只有一條語句,也可省略大括號,其實{}及其內部語句這個整體也可以看作為一個語句,叫塊語句while (循環條件)  ...;//某一條語句/<code>

關於大括號{}形成的塊語句,我將在後續作用域

相關的文章中提及。

例如:

<code>int i = 0;while (i < 10) {  ++i;}//也可寫成while (i < 10)  ++i;/<code>

do-while

我們先看下do-while的一般形式

<code>do {  ...//循環內語句} while (循環條件);/<code>

do-while與while的差別在於,循環條件的檢查是前置還是後置。

while的執行流程與上面的流程圖一致,即先驗證循環條件是否滿足,滿足則進入循環。

而do-while則是先執行循環內語句,然後檢查循環條件,滿足循環條件則繼續執行循環內語句,如此往復,直至循環條件不滿足時退出循環。

for

先看一下for循環結構的一般形式

<code>for (表達式1;表達式2;表達式3) {  ...//循環內語句}//如果循環內只有一條語句,可省略大括號for (表達式1;表達式2;表達式3)  ...;//某一條語句/<code> 

for循環中三個表達式均可以根據需求給出或者省寫。三個表達式的含義如下:

  • 表達式1——循環的前置處理,即在檢查循環條件前被執行,一般都是用來初始化循環條件相關的變量
  • 表達式2——循環條件,每一輪執行循環內語句前都要驗證表達式2的值是否為真,為真則執行循環內語句
  • 表達式3——循環後置處理,每一輪循環內語句執行後,在檢測循環條件(表達式2)前被執行,一般用於修改循環條件相關的變量值

看個例子:

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

這個例子中,利用for循環讓程序執行10次printf函數的調用,而printf將會在終端打印每一次循環時i的值。再來看幾個例子:

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

這兩個例子的功能與上一個代碼區的代碼功能完全一樣,只是for循環結構寫法略有不同而已。



控制結構

控制結構是用來對程序執行流程進行控制的,例如滿足什麼條件執行哪些語句。

看一下控制結構的一般流程

零基礎學C語言——循環與控制結構

控制結構流程圖

程序進入控制結構後一般先進行條件判斷,如果滿足條件則執行一段語句,如果不滿足條件則不執行語句或執行另一段語句。這類流程一般稱為

分支結構。除卻分支結構外,流程控制還包含一些其他功能。

在C語言中,流程控制包含如下內容:

  • if-else
  • switch
  • break
  • continue
  • goto

if-else

這是最典型的分支結構,其形式非常直觀。

假設我們定義瞭如下兩個變量:

<code>int a = 90, b = 80;/<code>

我希望a>b時向終端輸出a的值,那麼代碼可以寫成:

<code>if (a > b) {  printf("%d\\n", a);}/<code>

如果同時我希望a<=b時,輸出b的值呢?

<code>if (a > b) {  printf("%d\\n", a);} else {  printf("%d\\n", b);}/<code>

if-else語句的一般形式

<code>if (判斷條件) {  ...//條件成立時的一段語句} else {  ...//條件不成立時的一段語句}/<code>

其中,如果{}中只有一條語句,那麼大括號可以省寫。

if-else也可以嵌套,看下面這個例子:

<code>if (a > b) {  if (a-b > 10) { //潛入if判斷,a-b的值大於10則調用printf     printf("%d\\n", a-b);//打印a-b的值  }} else {  if (a%b == 1) { //此處大括號不可省寫,因為內部包含多於1條語句    a = 100;    printf("%d\\n", a%b);  } else //此處大括號可以省寫,因為只有一條函數調用語句    printf("%d\\n", b);}/<code>

如果我對a - b的結果有多種處理時,除了上述的嵌套,還可以怎麼寫呢?

<code>if (a-b == 10) {  printf("%d\\n", a);} else if (a-b == 9) {  printf("%d\\n", b);} else if (a-b == 8) {  printf("%d\\n", a);} else {  printf("%d\\n", b);}/<code>

這裡其實也是嵌套,因為else後跟的是一條if語句,因此大括號省略。將if直接寫在else後,代碼讀起來更容易理解。

switch

如果上例中的分支條件有很多的話,則會寫出一長串if-else,這樣的代碼會很蠢笨,有沒有更優雅的寫法呢?來一起看看switch吧,重寫上面a-b的例子:

<code>switch (a-b) {  case 10:    printf("%d\\n", a);    break;  case 9:    printf("%d\\n", b);    break;  case 8:    printf("%d\\n", a);    break;  default:    printf("%d\\n", b);    break;}/<code>

這段代碼的含義與上面的if-else版本的完全一樣,但這樣看著是不是更簡潔一些?

來看下switch的一般形式

<code>switch (表達式) {  case 數值1:    ...//一些語句  case 數值2: {    ...//一些語句  }  ...  default:    ...//一些語句}/<code>

表達式的值的數據類型必須為 ,且不能為指針類型。上例中,表達式a-b的值為整型。

switch中對每個分支做的都是等值判斷

case關鍵字後跟的數值,這些數值的數據類型都必須是 ,且不能為指針類型

上面的例子中用到了break,我們馬上就會說到。break本是用來跳出循環的,但也可用於switch結構中。如果a-b的例子(a=90, b=80)中不加入break,那麼終端輸出結果就會是:

<code>90809080/<code>

即,會從第一個滿足等值匹配的case處執行其中語句,並在下一個case處<strong>不進行等值判斷,直接執行其中語句,如此直至switch中的後續分支都被執行完。

此外,case後可以寫{}也可不寫,但這兩者的差別並不是在於case中語句數量,而是是否可以定義新的變量。

<code>switch (a - b) {  case 10:    int c = a - b;    break;  default:    break;}/<code>

這樣的寫法是<strong>不符合語法的,case內如果不加{},則不允許定義變量。如果一定要定義,可以如下寫:

<code>switch (a - b) {  case 10: {    int c = a - b;    break;  }  default:    break;}/<code>

break

在switch中提到過,break是用來跳出循環的,我們舉個例子:

<code>int i;for (i = 0; i < 10; ++i) {  if (i % 10 == 3)    break;}/<code>

這段代碼利用for循環結構做10次循環,但是我希望在第4次循環(i=3)時退出循環。我們可以利用if語句做判斷,然後利用break關鍵字配以分號所組成的語句跳出循環。

continue

除了跳出循環,有時我們發現循環中有一些變量的內容未達到預期時,希望暫時不執行循環內的一些語句處理。例如:

<code>int i = 0, j = 0;for (i = 0; i < 10; ++i, ++j) {//for中的是表達式,逗號表達式也可以被應用在此  if (j < 5)    continue;  j *= 10;}/<code>

這個例子中,我期望j在小於5時不要乘10,此時,我可以用if語句來判斷j的內容,如果小於5,則用continue關鍵詞配以分號所組成的語句,讓循環中的執行流程不繼續往下執行,而是直接走到for的第三個表達式處理,然後流程再進入for的第二個表達式判斷,滿足第二個表達式條件後繼續進入for中從頭執行語句,如此往復,直到j滿足條件後,才每輪循環都執行j *= 10;這條語句。

goto

有時,我們不在循環中時,也會有跳轉的需求,儘管這類需求極少。在日常工程中,也不推薦使用goto關鍵詞,因為濫用goto會讓代碼維護難度增加。

我們來看一個goto的例子:

<code>int main(void){  int a = 10;again:  ++a;  if (a < 12)    goto again;  return 0;}/<code>

這裡,again是一個標號(label),其命名規則與變量命名規則一致,其後必須跟隨冒號。標號在其所在作用域(即其所在函數)內唯一。

goto的必須配合label一同使用,因為編譯器需要知道流程跳轉到什麼位置。

上面的這段代碼的含義就是,定義了整型變量a,然後a自加,判斷a的值是否小於12,小於的話,跳轉到again的位置繼續執行其後的語句(也就是從++a;開始的部分)。如果a >= 12了,那麼正常返回。

一個綜合使用的例子

<code>#include <stdio.h>int main(void){    int i;    char s[] = "Hello World";    for (i = 0; i < sizeof(s); ++i) {        if (s[i] == '\\0')            break;        switch (s[i]) {            case 'H':            case 'W':                printf("up-case\\n");                break;            case ' ':                printf("blank\\n");                break;            default:                printf("low-case\\n");                break;        }    }    return 0;}/<stdio.h>/<code>

其中,case部分如果不寫任何語句,那麼流程在匹配後會繼續向下一個case前進,但不會對下一個case的值進行等值驗證,直接進入其語句部分執行。


今天所提及的這些循環結構與控制結構都是可組合使用的。學習語言要將知識點融會貫通,這需要一個過程,需要多進行嘗試,嘗試出錯不要怕,想清原因並嘗試如何修正將會大大提升編程水平與信心。


喜歡的小夥伴可以關注碼哥,也可以給碼哥留言評論,如有建議或者意見也歡迎私信碼哥,我會第一時間回覆。


分享到:


相關文章: