了解了各種表達式和語句之後,就相當於掌握了寫作文要用到的詞語和句子,但是,僅有詞語和句子是無法構成一篇有意義的文章的。要完成一篇文章,先需要確定這篇文章的結構,是先分述再總述,還是逐層遞進論述。定好結構後再按照結構的要求將詞語和句子安排到合適的位置,這樣才能寫出一篇有意義的詞句通順的文章。編寫程序就像寫文章一樣,也同樣需要先根據需要處理的事務確定程序的流程控制結構,然後再將那些零散的語句串聯起來描述一個完整的處理事務的過程,從而將多條零散的語句組織成可以完成一定功能的完整程序。
按照處理事務的流程的不同,C++中主要有下面三種程序流程控制結構:
1. 順序結構
順序結構是C++中最簡單也最常用的一種流程控制結構。這種結構在執行的時候,它按照從上往下的順序,依次逐條執行程序的語句。在整個過程中,執行順序不會發生改變。所以,它通常被用來組織那些只需要一步步順序執行就可以完成任務的語句。比如,一個簡單的加法計算過程,從輸入到計算再到輸出,只需要依次順序執行就可以完成,所以我們使用順序結構來將那些分別負責輸入(cin>>a>>b;)、計算(int c = a + b;)和輸出(cout<<a<<" + "<<b<<" = "<<c<<endl;)的語句組織起來,就成了一個簡單的加法計算程序:
// 順序結構組織的加法計算程序 int main() { cout<<"請輸入兩個整數:"<<endl; int a,b; cin>>a>>b; // 輸入 int c = a + b; // 計算 cout<<a<<" + "<<b<<" = "<<c<<endl; // 輸出 return 0; }
在執行的時候,從進入main()函數開始,首先執行輸入語句(cin>>a>>b;),獲得用戶輸入的兩個數值a和b,然後依次往下執行計算語句(int c = a + b;),獲得計算結果並保存到變量c,接下來執行輸出語句(cout<<a<<" + "<<b<<" = "<<c<<endl;),將計算結果輸出。這樣,使用順序結構組織的各條語句,只需要順序執行就可以完成任務。
2. 選擇結構
現實世界是復雜的,很多問題並不是只用順序步驟就可以解決。還是以上面的求兩個數之和的程序為例,如果要求輸入的數必須大於0,也就是只有大於0的兩個數才能計算它們的和。這時就需要先判斷輸入的數是否大於0,如果滿足條件,就計算兩個數的和;如果不滿足,就提示用戶輸入的數不滿足條件,無法計算。為了表達這種根據不同條件擁有不同處理方法的過程,C++提供了選擇結構。選擇結構依靠if等條件語句來實現,它可以根據不同條件做出判斷,選擇不同的執行路徑,實現不同的處理過程。例如:
// 用選擇結構實現的加法計算程序 int a,b; cin>>a>>b; // 選擇結構,根據條件不同,執行的路徑也不同 if( a > 0 && b > 0 ) { // 如果用戶輸入的數據滿足條件, // 則計算結果並輸出 int c = a + b; cout<<a<<" + "<<b<<" = "<<c<<endl; } else { // 如果用戶輸入的數據不滿足條件, // 則提示用戶輸入錯誤 cout<<"請輸入兩個大於0的數。"<<endl; }
利用選擇結構,我們將對滿足條件的情況進行處理的語句組織到if分支,將對不滿足條件的情況進行處理的語句組織到else分支,這樣程序在執行的時候,就會根據滿足條件與否而執行不同的代碼,實現了對“根據不同條件擁有不同處理方法”的表達。
3. 循環結構
再來看另外一種更復雜的情形:要求輸入100個數後計算它們的總和。如果采用前面介紹的順序結構,那麼就要定義100個變量,用到100個輸入語句來接收數據,同時也需要100個加和語句來將它們加到總和中。這麼繁瑣的計算過程顯然是無法接受的,那麼有什麼方法可以簡化一下這個計算過程呢?
人們發現這類計算過程有一個規律:這個計算過程中的多次輸入和加和計算的代碼是相似的,輸入都是從cin獲取輸入數據到某個變量,而加和計算也都是將輸入的數據累加到一個表示總和的變量上。正因為如此,在完成一次輸入和加和計算後,可以利用相同的代碼完成第二次輸入和加和計算。這樣,整個計算過程就成了一個輸入和加和計算不斷重復循環往復的過程。為了表達這一類的計算過程,C++提供了循環結構。循環結構依靠for等循環語句來實現,我們將那些需要重復多次執行的語句作為循環體語句,從而可以在循環過程中得到重復多次執行,以此來表達那些同一動作需要反復執行的計算過程。 在這裡,負責輸入(cin>>n;)和加和計算(nTotal += n;)的語句是需要重復多次執行的,所以我們將它作為for循環的循環體語句,然後在for循環中確定循環的起點和終點,以此串聯成一個可以輸入並計算100個數之和的程序:
// 用循環結構實現的連續加法計算程序 // 表示總和的變量 int nTotal = 0; // for循環實現的循環結構 for(int i = 0; i < 100; ++i) { int n = 0; cin>>n; // 輸入語句 nTotal += n; // 加和計算語句 } // 輸出結果 cout<<"你輸入的100個數的總和是:"<<nTotal<<endl;
在執行for循環的時候,會連續100次地執行循環體中的輸入語句和加和計算語句,這樣就省掉了使用順序結構可能帶來的繁瑣,很好地表達了“某個動作需要反復多次執行”的計算過程。
順序結構、選擇結構和循環結構是三種最基本的程序控制結構。這三種結構不僅可以單獨使用,更多時候,我們是將多種控制結構組合使用,以此來表達那些更加復雜的計算過程。例如在上面的例子中,如果我們只想要計算輸入的所有正數的和,我們就可以在循環結構中嵌入一個選擇結構,用循環結構來組織那些需要反復多次執行的動作,而每次循環執行的時候,都先用選擇結構對數據進行篩選,只有符合條件的數才進行加和計算。循環結構和選擇結構的嵌套就可以表達這一更加復雜的計算過程:
// 循環結構和選擇結構的嵌套使用 // 循環結構,執行重復動作 for(int i = 0; i < 100; ++i) { int n = 0; cin>>n; // 選擇結構,篩選數據,對符合條件的數據進行計算 if( n > 0) { nTotal += n; } }
通過這三種控制結構的組合,幾乎可以描述任何復雜的程序執行流程。換句話說,也就是無論什麼樣的程序執行流程,無外乎是這三種結構或這三種結構的組合。而正是這三種控制結構將零散的實現簡單操作的語句組織起來,表達一定的運算過程,最終才形成了實現一定功能的程序。
剛看過《你好,C++!》的前面幾個章節,我們的“程序員”小陳就覺得自己的C++已經掌握得很不錯了。很快,他就應聘到了一家軟件公司做了一名程序員。
上班第一天,老板就給他分配了一個“大”任務:
“小陳啊,今天雖然是你上班的第一天,但公司現在有個大任務需要你去完成。你知道的,我們公司也是一個擁有近100000名員工的大公司。現在需要一個程序來對員工的工資進行統計和查詢。統計就是需要知道所有員工的最低工資、最高工資和平均工資。查詢就是需要可以根據員工的序號查詢得到員工的工資。你的C++水平那麼高,完成這樣一個工資程序應該沒有問題吧?”
“沒,沒,一點問題都沒。”
小陳越聽越心驚,到目前為止,雖然看過幾天《你好,C++!》,但他還從來沒有寫過如此復雜的C++程序,心中實在沒底,只好先答應下來,然後再回去看書想辦法。等到小陳將《你好,C++!》再次仔細地看過一遍後,他驚喜地發現,要完成這個工資程序所需要的知識在書中早有論述:要對多個相同性質的批量數據(將近100000個工資數據)進行管理,可以采用數組;要多次重復執行某個動作(輸入和查詢工資數據),可以使用循環結構來組織語句;要根據不同條件執行不同的動作(如果輸入特殊的數值-1,表示工資查詢結束;如果輸入的序號超出序號范圍,提示用戶重新輸入;如果輸入的序號在序號范圍內,輸出相應序號員工的工資),可以使用選擇結構來實現不同的程序執行路徑。經過簡單的分析,小陳發現這個程序從整體而言是一個順序結構:先輸入數據,然後對數據進行查詢;而每一個步驟又是一個循環結構:利用for循環多次輸入數據,利用while循環多次查詢數據;同時在循環結構中,又會嵌套選擇結構,根據用戶輸入的不同執行不同的動作。按照這樣的分析結果,小陳很快有了下面的工資程序:
// 工資程序 V1.0 #include <iostream> #include <climits> // 為了使用整數最值宏INT_MAX,INT_MIN using namespace std; int main() { // 定義數組的最大數據元素量, // 表示這個程序最多可以處理100000個工資數據 const int MAX = 100000; // 定義數組並初始化,這個數組可以包含100000個int類型工資數據 int arrSalary[MAX] = {0}; // 定義記錄工資總值、最小值和最大值的變量 // 因為min和max用於記錄最小值和最大值, // 所以我們分別將其初始化為int類型數據的最大值和最小值 int nTotal = 0; int min = INT_MAX; int max = INT_MIN; // 輸入的有效工資數據個數,計算平均工資和查詢工資時需要 int nCount = 0; // 利用for循環結構,重復執行輸入動作,完成多個工資數據的輸入 for(int i = 0; i < MAX; ++i) { // 提示用戶輸入 cout<<"請輸入"<<i<<"號員工的工資(-1表示輸入結束):"<<endl; // 將輸入的數據保存到arrSalary數組的第i個數據元素 cin>>arrSalary[i]; // 利用條件結構,檢查是否輸入了表示結束的特殊值 if(-1 == arrSalary[i] ) { // 輸入結束,輸出統計結果 cout<<"工資輸入結束,一共輸入了"<<nCount<<"個工資數據。"<<endl; // 如果輸入的數據個數不為0,輸出統計信息 if(0 != nCount) { cout<<"其中,"<<endl; cout<<"最大值是"<<max<<endl; cout<<"最小值是"<<min<<endl; // 計算平均工資 float fAver = (float)nTotal/nCount; cout<<"平均工資是"<<fAver<<endl; } // 輸入結束,用break關鍵字結束整個輸入循環 break; } // 如果是正常輸入,則進行常規處理 ++nCount; // 工資數據個數加1 // 累加工資總額 nTotal += arrSalary[i]; // 更新工資的最大值和最小值 if(arrSalary[i] < min) // 如果新輸入的數值比已知的最小值min小 min = arrSalary[i];// 用新輸入的值取代舊的最小值 if(arrSalary[i] > max) // 最大值相似處理 max = arrSalary[i]; } // 輸入過程結束,開始查詢過程 // 如果數據個數為0,表示沒有輸入數據,不再進行查詢而直接結束 if(0 == nCount) { cout<<"沒有工資數據,無法進行查詢。感謝使用!"<<endl; return 0; // 直接結束程序 } // 擁有工資數據,構造無限循環進行工資查詢,在循環中根據條件退出循環 while(true) { // 輸入的員工序號 int n = 0; // 提示用戶輸入 cout<<"請輸入要查詢的員工序號(0-"<<nCount-1 <<",-1表示結束查詢):"<<endl; // 獲取用戶輸入的員工序號並保存到n cin>>n; // 對用戶輸入進行檢查 if(-1 == n) // 是否輸入了表示結束的特殊值 { // 查詢結束,用break關鍵字結束整個查詢循環 cout<<"查詢完畢,感謝使用!"<<endl; break; } else if(n < 0 || n >= nCount) // 是否超出序號范圍 { // 提示用戶輸入錯誤 cout<<"輸入的序號"<<n<<"超出了序號范圍0-" <<nCount-1<<",請重新輸入。"<<endl; // 輸入的序號超出范圍, 用continue結束本次循環,開始下一次循環 continue; } // 輸入合法,輸出用戶查詢的員工工資 cout<<"員工序號:"<<n<<endl; // 這裡用輸入的員工序號作為數組下標,直接得到對應位置上的工資數據 cout<<"員工工資:"<<arrSalary[n]<<endl; } return 0; }
在簡單試用幾次之後,小陳對這個工資程序還算滿意。於是他趕緊把這個程序拿去向老板交差。老板試了試發現,這個工資程序不僅能夠處理足夠多的工資數據,能夠對工資數據進行統計和查詢,同時還有非常好的用戶操作提示,連三歲小孩子都會用。老板非常高興,喜笑顏開地對小陳說:“干得不錯,下個月,漲工資,啊哈哈哈…… ”聽到這句話,小陳在心裡樂開了花,心中暗想,辛虧手頭有一本《你好,C++!》,問題才能夠如此順利地被解決。看來這真是一本好學又好用的C++參考書,下班回去之後一定接著往下看……