550 likes | 706 Views
Computer Programming - C. Andres, Wen-Yuan Liao Department of Computer Science and Engineering De Lin Institute of Technology andres@dlit.edu.tw http://www.cse.dlit.edu.tw/~andres. 目標 了解基本解決問題的技巧 能夠發展出完整的從上到下,可進行逐步細部修改過程的演算法 能夠使用選擇敘述式 if , if/else 來 選擇要執行的動作 能夠使用 while 重複結構, 來 重複執行一些敘述式
E N D
Computer Programming - C Andres, Wen-Yuan Liao Department of Computer Science and Engineering De Lin Institute of Technology andres@dlit.edu.tw http://www.cse.dlit.edu.tw/~andres
目標 了解基本解決問題的技巧 能夠發展出完整的從上到下,可進行逐步細部修改過程的演算法 能夠使用選擇敘述式if, if/else來選擇要執行的動作 能夠使用while重複結構,來重複執行一些敘述式 了解計數器控制結構和旗標 (警示)訊號控制結構 能夠使用遞增、遞減、指定和邏輯運算子 Chapter 3 –結構化程式的開發
Chapter 3 –結構化程式的開發 大綱 3.1簡介3.2演算法3.3虛擬碼3.4控制結構3.5 if選擇結構3.6if/else選擇結構 3.7while重複結構 3.8 演算法的規劃:範例研究一 (計數器控制迴圈Counter-Controlled Repetition)3.9從上到下,逐步細部修改的演算法規劃:範例研究二 (旗標訊號控制迴圈Sentinel-Controlled Repetition) 3.10 從上到下,逐步細部修改的演算法規劃:範例研究三 (巢狀控制結構Nested Control Structures)3.11 指定運算子3.12 遞增和遞減運算子
3.1 簡介 • 在寫程式之前: • 對問題有個通盤的了解 • 仔細規劃解決此問題的方式 • 開始寫程式時: • 需要知道有哪些「程式區段」可以使用 • 要使用好的程式設計觀念
3.2 Algorithms演算法 • 所有的計算問題 • 可藉由執行一系列經特定安排的動作加以解決。 • 解決問題的程序即所謂的 Algorithm演算法 • 包括: • 將要作的動作) • 這些動作的執行順序 • Program control程式 控制 • 指定程式中敘述式的執行順序 起床、脫掉睡衣、洗澡、穿衣服、吃早餐、開車上班 起床、脫掉睡衣、穿衣服、洗澡、吃早餐、開車上班
3.3 Pseudocode虛擬碼 • Pseudocode(虛擬碼) • 是給人看的、非正規的語言,主要功用是發展演算法 • 類似日常用的英文 • 並不能在電腦上實際執行 • 其功用是幫助程式設計者在寫程式之前,先「想出」程式該怎麼寫。 (這是演算法、虛擬碼的目的) • 可以很容易地轉換為相對的 C 程式 • 只由可以執行的敘述式組成 (不包含變數宣告)
3.4 Control Structures控制結構 • Sequentialexecution循序式執行 • 敘述式按順序從上到下執行。 • Transfer of control控制權轉移 • 接下來要執行的敘述式不按順序 • 早期控制權轉移的指令是 goto:可轉移到(幾乎)任何地方去。 • Structured programming ≡ gotoelimination (消除goto) • Bohm 和 Jacopini證明所有程式都可只用三種控制結構寫 • Sequencestructure控制結構 • C 內建的執行方式 • Selectionstructures 選擇結構 • C有三種型態 - if, if/else, 與 switch • Repetitionstructures 重複結構 • C有三種型態 - while, do/while, 與 for
3.4 Control Structures控制結構 • Flowchart 流程圖 • 用圖形來表示演算法 • 用一些特殊涵義的圖示(如:矩形、菱形、橢圓、小圓圈)表示所要執行的動作、用箭號連接。 • 矩形表示動作(任何型態的動作,包括輸入、輸出) • 橢圓形表示程式的開始與結束 • 小圓圈表示一段程式的開始與結束 • 單一入口/單一出口 的控制結構 • 將一個控制結構的出口點接到下個控制結構的入口 (堆疊式控制結構). • 使程式更容易建立
true false print “Passed” grade >= 60 3.4 Control Structures控制結構 C循序結構流程圖 C選擇結構流程圖
3.5 if選擇結構 • 選擇結構 • 選取不同功能的動作 • 虛擬碼的例子: If student’s grade is greater than or equal to 60 Print “Passed” • 若條件為 true • print 敘述式會被執行,之後再執行下個敘述。 • 若條件為 false • print 敘述式會被忽略,直接執行下個敘述。 • 縮排比較容易閱讀 • C 的編譯器會忽略空白字元(whitespace characters),包括:空白、定位(tab)、新行(newline)等字元。
3.5 if 選擇結構 • 將虛擬碼轉換為 C敘述: if ( grade >= 60 ) printf("Passed\n"); • 菱形符號 (decision symbol) • 表示要作決定 • 包含要作判斷的運算式(條件) • 測試條件是否成立、執行適當的敘述 • if結構是單一入口/單一出口的結構 • C語言中的條件是否成立:以運算式結果為 0 的話就代表不成立(偽),否則就成立(真)。
運算式結果代表: 零 - false 非零 - true 例如: 3 - 4是true true false print “Passed” grade >= 60 3.5 if 選擇結構 • 前面虛擬碼的流程圖 • 良好的程式設計習慣 3.1-3.2 • 在程式中採用前後一致的縮排方式,可以大幅增加程式的可讀性。建議用固定大小的內縮量( ¼ 英吋或3格) • 虛擬碼在程式設計階段常用來「想出」程式的架構。然後再將虛擬碼的程式轉換成 C 程式碼。
3.6 if/else選擇結構 • if • 只有當條件成立時才執行某個動作 • if/else • 當條件成立執行一個動作,條件不成立執行另一個動作 (two-way selection 二選一) • 虛擬碼 if student’s grade is greater than or equal to 60print “Passed” else print “Failed” • C 程式碼 if ( grade >= 60 ) printf("Passed\n"); else printf(“Failed\n");
false true print “Passed” print “Failed” grade >= 60 3.6 if/else選擇結構 • 三元條件運算子 (?:) • 有三個引數、運算元 (條件, value if true, value if false) • 前面的虛擬碼可改寫成: Printf(“%s\n”, grade >= 60 ? “Passed” : “Failed” );
3.6 if/else選擇結構 • 巢狀 if/else結構 (重疊式) • 將if/else放在另一個 if/else選擇結構中,可以測試多種狀況,達到多選一的效果 if student’s grade is greater than or equal to 90 Print “A”else if student’s grade is greater than or equal to 80 Print “B” else if student’s grade is greater than or equal to 70 Print “C” else if student’s grade is greater than or equal to 60 Print “D” else Print “F” • 只要一個條件成立、其餘的都被跳過 P.21
3.6 if/else選擇結構 if (grade >= 90) printf( “A\n”); else if (grade >= 80) printf( “B\n”); else if (grade >= 70) printf( “C\n”); else if (grade >= 60) printf( “D\n”); else printf( “F\n”);
3.6 if/else選擇結構 if (grade >= 90) printf( “A\n”); elseif (grade >= 80) printf( “B\n”); elseif (grade >= 70) printf( “C\n”); elseif (grade >= 60) printf( “D\n”); else printf( “F\n”); • 多選一的寫法。
3.6 if/else選擇結構 • Compoundstatement(複合敘述式) • 將好幾個敘述用大括號包起來,成為一個複合敘述式 • 例如:: if ( grade >= 60 ) printf( “Passed.\n” ); else { //要記得加括號printf( "Failed.\n“ ); printf( "You must take this course again.\n“ );} • 如果沒有加大括號的話 printf( "You must take this course again.\n“ ); 就一定會被執行。 • Block • 複合敘述式加上變數宣告
3.6 if/else選擇結構 • Syntax errors // 語法錯誤,compiler 看不懂。 • 由編譯器引起的錯誤 • Logic errors //邏輯錯誤,執行結果有錯誤 • Errors which have their effect at execution time • Non-fatal logic errors //非致命錯誤,可執行完,結果錯誤 • program runs, but has incorrect output • Fatal logic errors //致命錯誤,執行到一半被強迫停止。 • program exits prematurely
良好的程式設計習慣 3.3-3.4 • 將 if/else結構的兩個本體敘述式都予以縮排處理 • 如果需要安排好幾層的縮排,每層都用相同的縮排量 軟體工程的觀點 3.1 • 複合敘述式(compound statement)可以放在任何單行敘述式可出現的地方。
常見的程式設計錯誤 3.1-3.2 • 如果忘記加上大括號的任一邊,就無法限制複合敘述式的範圍,就會導致程式出現語法錯誤或者邏輯錯誤。 • 若在if結構的條件式後面多放一個分號,這會讓單選擇的if結構產生邏輯錯誤,會讓雙選擇的 if結構產生語法錯誤(若在 if的部份包含一個實際的敘述式). 避免錯誤的小技巧 3.1 • 有些程式設計者寫程式時,會先將複合敘述的左右大括號一次先打好,可避免遺漏的情形。
3.7 while 重複結構 • 重複結構 • 程式設計者指定在某條件持續成立時、重複執行某個動作 • 虛擬碼 while there are more items on my shopping list Purchase next item and cross it off my list • while迴圈一直重複到條件不成立為止 • 例如: int product = 2; while ( product <= 1000 ) product = 2 * product; while 的 body 可以是單一的 statement 或是一個 compound statment product最後變成 1024 才結束。
true product <= 1000 product = 2 * product false 3.7 while 重複結構 • while迴圈的流程圖 • 常見的程式設計錯誤 3.3-3.4 • 若在while結構的本體裡面沒提供最後會造成while條件變成false的動作,這一般會產「無窮迴圈」的錯誤,也就是重複結構永遠不會終止。 • 若將關鍵字 while拼成大寫字母的While(記得 C 是大小寫有別的語言),就是語法錯誤。所有 C 的關鍵字都只包含小寫字母,例如:while, if和 else。
3.8 演算法的規劃:範例研究一 (計數器控制迴圈Counter-Controlled Repetition) • Counter-controlled repetition 計數控制重複結構 • 迴圈重複執行到計數器達到特定值為止 • Definite repetition 明確的重複 • 重複次數事先知道 • 例如: 十個人的班級舉行小考,輸入每個人的分數(0-100的整數),請計算這次小考的平均分數。
3.8 Formulating Algorithms (Counter-Controlled Repetition) • 此範例的虛擬碼 Set total to zero Set grade counter to one While grade counter is less than or equal to ten Input the next grade Add the grade into the total Add one to the grade counter Set the class average to the total divided by tenPrint the class average • 下面是此範例的 C 程式碼
迴圈每執行一次 counter 就增加 1,最後 counter 的值導致迴圈結束
Enter grade: 98Enter grade: 76Enter grade: 71Enter grade: 87Enter grade: 83Enter grade: 90Enter grade: 57Enter grade: 79Enter grade: 82Enter grade: 94Class average is 81 總分是 817,但平均為整數,所以結果是 81。 • 常見的程式錯誤 3.5 • 若 counter 或 total 沒有作初始化,你程式的結果很可能不正確,這是邏輯錯誤。
3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈)3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈) • 讓我們將前面的問題一般化 Develop a class-averaging program that will process an arbitrary number of grades each time the program is run. 寫一個計算全班平均分數的程式,班級的人數可以任意 • 不知道學生人數 - 如何知道程式何時要停止? • Sentinel value(警示值) • 指出資料結束 • 當警示值被輸入時,迴圈也必須結束 • 選擇一個適當的值當警示值,警示值必須與正常的資料不同(這例子中可以用 –1 當警示值)
3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈)3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈) • 從上到下、逐步細部修改(Top-down, stepwise refinement) • 一開始只用單獨一行敘述如下: Determine the class average for the quiz(計算小考全班平均分數) • 分成比較小的工作、並按順序寫下來: Initialize variables(初始化變數) Input, sum and count the quiz grades(輸入、計算人數、總分) Calculate and print the class average (計算並印出平均分數)
3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈)3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈) • 很多程式都可以分成三個部份 • Initialization 初始化 • 將程式變數作初始化 • Processing 處理 • 輸入資料、並適當地調整變數內容 • Termination 停止 • 計算並印出最後結果 • 將初始化的步驟分成更小的細節 Initialize variables to Initialize total to zero (total 初始設為 0) Initialize counter to zero (counter 初始設為 0)
3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈)3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈) • 改進 Input, sum and count the quiz grades to Input the first grade (possibly the sentinel) While the user has not as yet entered the sentinel Add this grade into the running total Add one to the grade counter Input the next grade (possibly the sentinel) • 改進 Calculate and print the class average to If the counter is not equal to zero Set the average to the total divided by the counter Print the average Else Print “No grades were entered”
3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈)3.9 利用從上到下,逐步細部修改的演算法規劃:範例研究二(警示訊號控制迴圈) Initialize total to zero Initialize counter to zero Input the first grade While the user has not as yet entered the sentinel Add this grade into the running total Add one to the grade counter Input the next grade (possibly the sentinel) If the counter is not equal to zero Set the average to the total divided by the counter Print the average else Print “No grades were entered”
(float) – 強制型哢轉換運算子。將 total暫時當成 float來對待 這是必須的,因為兩個整數相除的結果也是整數,小數部份會被自動捨棄 counter是 int, 但在這裡會被升級為float. f – 用浮點數的十進位小數點方式輸出數字 .2 – 指定小數列印的精確度。列印到小數點後2位.
Enter grade, -1 to end: 75 Enter grade, -1 to end: 94 Enter grade, -1 to end: 97 Enter grade, -1 to end: 88 Enter grade, -1 to end: 70 Enter grade, -1 to end: 64 Enter grade, -1 to end: 83 Enter grade, -1 to end: 89 Enter grade, -1 to end: -1 Class average is 82.50 Enter grade, -1 to end: -1No grades were entered
常見的程式設計錯誤 3.6~3.9 • 若選取的警示值也是合法的資料數值,就是邏輯錯誤。 • 企圖除以 0 時會造成致命錯誤。 • 使用浮點數時假設它是用精確的方式來儲存會造錯誤的結果,大部份電腦的浮點數是用近似的方式儲存。 • 良好的程式設計習慣 3.5-3.6 • 執行除法時,若除數(式)的值可能為 0,請以適合的方式明確地先測試這個狀況是否發生(就可在錯誤發生時,印出錯誤訊息),而不是任由發生致命錯誤。 • 在利用旗標(警示值)訊號控制迴圈的結構中,要求使用者輸入資料的提示,必須明確的提醒使用者規定的旗標值是多少。
避免錯誤的小技巧 3.3 • 不要比較兩個浮點數是否相等或不相等,而是比較兩者的差是否小於某個指定的小數值範圍。
3.10 從上到下,逐步細部修改的演算法規劃:範例研究三 (巢狀控制結構) • 問題: A college has a list of test results (1 = pass, 2 = fail) for 10 students. Write a program that analyzes the results. If more than 8 students pass, print “Raise Tuition“(調高學費). • 我們可觀察到: • 這個程式必須處理10個測驗結果,可用計數控制迴圈 • 每個測驗結果都是數字 — 不是 1就是 2,若輸入不是 1, 就假設是 2。 • 用兩個計數器 - 分別計算 passed 與 failed 的人數 • 在程式完成所有的考試結果後,必須判斷是否有8位以上的學生通過考試。 • 開始分析的虛擬碼: Analyze exam results and decide if tuition should be raised (分析考試結果並決定學費是否調漲)
3.10 從上到下,逐步細部修改的演算法規劃:範例研究三 (巢狀控制結構) • 第一次細部修改 Initialize variables Input the ten quiz grades and count passes and failures Print a summary of the exam results and decide if tuition should be raised • 第二次細部修改 Initialize variables to Initialize passes to zero Initialize failures to zero Initialize student counter to one
3.10 從上到下,逐步細部修改的演算法規劃:範例研究三 (巢狀控制結構) • 第二次細部修改 Input the ten quiz grades and count passes and failures to While student counter is less than or equal to tenInput the next exam result If the student passed Add one to passesElse Add one to failures Add one to student counter • 第二次細部修改 Print a summary of the exam results and decide if tuition should be raised to Print the number of passes Print the number of failures If more than eight students passed Print “Raise tuition”
3.10 從上到下,逐步細部修改的演算法規劃:範例研究三 (巢狀控制結構) Initialize passes to zero Initialize failures to zero Initialize student to one While student counter is less than or equal to ten Input the next exam result If the student passed Add one to passes else Add one to failures Add one to student counter Print the number of passes Print the number of failures If more than eight students passed Print “Raise tuition”
增進效能的小技巧 3.1 • 在變數宣告時順便初始化,可以避免忘記作初始化所帶來的問題。 軟體工程的觀點 3.7~3.8 • 經驗顯示利用電腦解決問題最困難的地方是,如何設計出該問題的演算法,一旦演算法想出來,就可以從演算法直接導出可執行的 C 程式。 • 很多有經驗的程式設計者寫程式時都不用虛擬碼,他們認為最終目的就是在電腦上解決問題,寫虛擬碼只是浪費時間。這種想法對一些簡單和常見的問題是實情,但是當要寫大型或複雜的系統時,就會導致嚴重的錯誤和時間的耽擱。
Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 2 Enter Result (1=pass,2=fail): 2 Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 2 Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 1 Enter Result (1=pass,2=fail): 2 Passed 6 Failed 4 Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 2Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Enter Result (1=pass,2=fail): 1Passed 9Failed 1Raise tuition
3.11 Assignment Operators • Assignment operators abbreviate assignment expressions c = c + 3; can be abbreviated as c += 3; using the addition assignment operator • Statements of the form variable=variableoperatorexpression; can be rewritten as variableoperator=expression; • Examples of other assignment operators: d -= 4 (d = d - 4) e *= 5 (e = e * 5) f /= 3 (f = f / 3) g %= 9 (g = g % 9)
3.11指定運算子 • 指定運算式縮寫 c = c + 3;可以縮寫為 c += 3;使用加法指定運算子 • 指定運算子的格式 variable = variable operator expression; 可寫成 variable operator= expression; • 其他指定運算子的例子如下:
3.12遞增和遞減運算子 • 遞增運算子 (++) : 可以用來取代 c += 1 • 遞減運算子 (--) : 可以用來取代 c -= 1 • 前置遞增(減) • 運算子放在變數前面 (++c或 ––c) • 變數內容先遞增(減),然後才計算運算式的結果 • 後置遞增(減) • 運算子放在變數後面 (c++或 c--) • 運算式的內容先計算出來,再將變數加(減) 1 • 例如:若 c = 5則 • printf(“%d\n”, ++c); • 會輸出 6 (c先改變, 再用printf輸出) • printf(“%d\n”, c++); • 會輸出 5 (printf先執行後, c的內容再增加,此敘述執行完後 c的值變成 6)