1.展示PTA總分
順序結構:
分支結構:
2.本章學習總結
2.1. 學習內容總結
1.數據類型
C語言的3種基本數據類型是整型、字符型和浮點型:
- 整型 int 取值范圍:-2147483648~+2147483648
- 短整型 short 取值范圍:-32768~+32768
- 長整型 Long 取值范圍:-2147483648~+2147483648
- 字符型 char 取值范圍:-128~+127
- 單精度浮點型 float 取值范圍:-/+3.4e38
- 雙精度浮點型 double 取值范圍:-/+1.7e308
根據MOOC的學習,在沒有特殊需要的情況下,整型變量優先使用int,浮點型優先使用double,操作變量時,需要非常注意數據是否會溢出。
2.格式化輸出與輸入
- 格式化輸出函數printf( );:該函數在系統文件stdio.h中聲明,一般調用格式為“printf(格式控制字符串,輸出參數1…輸出參數2);”格式控制字符串用雙引號括起來,表示輸出的格式,輸出參數是一些要輸出的數據,可以是常量、變量或表達式。在輸出格式控制說明中,可以加寬度限制詞,制定數據的輸出寬度,以達成一些題目的格式要求,或使輸出內容變得整齊、清晰。
- 格式化輸入函數scanf( );:該函數同樣在系統文件stdio.h中聲明,一般調用格式“scanf(格式控制字符串,輸入參數1…輸入參數2);”格式控制字符串用雙引號括起來,表示輸入的格式,輸入參數必須和格式控制字符串中的格式控制說明相對應,並且類型、個數和位置要一一對應。在需要輸入的變量前一定要加上“&”進行取地址運算,否則會出現變量無法讀入的情況。
- 字符輸入函數getchar( ):設ch是字符型變量,該函數一般調用格式為“ch = getchar( );”功能是從鍵盤輸入一個字符,並賦值給變量ch,但是該函數只能讀入一個字符。
- 字符輸出函數putchar( ):設ch是字符型變量,該函數一般調用格式為“getchar(輸出參數);”功能是輸出輸出參數的字符型變量或常量,該函數只能輸出一個字符。
3.if-else語句
該語句的一般形式為
if ( 表達式 )
{
語句1;
}
else
{
語句2;
}
該語句用於實現分支結構,首先求解表達式,如果表達式的值為“真”,則執行語句1;如果表達式的值為“假”,則執行語句2,應用舉例:c02-選擇結構 7-2 判斷偶數
4.for循環語句
使用該語句可以實現C語句的重復執行,一般形式為:
for ( 表達式1; 表達式2; 表達式3)
{
循環體語句;
}
for語句的執行流程是:先計算表達式1,再判斷表達式2,若為“真”,則執行循環體語句,並接着計算表達式3,然后繼續循環,若值為“假”,則結束循環,繼續執行for的下一條語句。
- 表達式1:初值表達式,對循環變量賦初值,從而指定循環的起點。
- 表達式2:條件表達式,給出循環繼續的條件。
- 表達式3:步長表達式,這個表達式是單次循環結束后需要執行的語句。
- 循環體語句:每一次循環需要執行的語句。
應用舉例:2840大題庫 實驗2-3-1 -for 求1到100的和
5.常用的數學函數
- 平方根函數sqrt(x):計算\(\sqrt{x}\)。如是sqrt(4.0)的值為2.0。
- 絕對值函數fabs(x):計算|x|.如fabs(-3.56)的值為3.56。
- 冪函數pow(x,n):計算\(x^n\)。如pow(1.1,2)的值為1.21。
- 指數函數exp(x):計算\(e^x\)。如exp(2.3)的值為9.974182。
- 以e為底的對數函數log(x):計算\(\log{x}\)。如log(123.45)的值為4.815836。
- 根據老師的講解,由於函數pow(x,n)內部是以循環的形式實現功能,因此如果在單個代碼中多次調用該函數,效率會被明顯降低,在下面的題目講解中我會用實例來說明這個問題。
6.多分支結構
多分支結構有兩種實現方式,分別是else-if語句和switch語句
else-if語句
一般形式為:
if (表達式1)
{
語句1;
}
…
else if (表達式n-1)
{
語句n - 1;
}
else
{
語句n;
}
它的執行流程為:首先求解表達式1,如果表達式1的值為“真”,則執行語句1,並結束整個if語句的執行,否則,求解表達式2……最后的else處理給出條件都不滿足的情況,即表達式1,表達式2……表達式(n-1)的值都為“假”時,執行語句n。
switch語句
一般形式為:
switch (表達式)
{
case 常量表達式1:語句段1; break;
…
case 常量表達式n:語句段n;break;
default:語句段n+1;break;
}
該語句的執行流程為:首先求解表達式,如果表達式的值與某個常量表達式的值相等,則執行該常量表達式后的相應語句段,如果表達式的值與任何一個常量表達式都不相等,則執行default后的語句段,最后執行break;語句,跳出switch語句。應用舉例c02-選擇結構 7-9 成績轉換
- switch語句中的常量表達式必須是常量。
else-if語句和switch語句的不同點
- switch語句實現的是一個跳轉作用,直接跳轉到符合條件的代碼去執行,僅進行1次判斷,在else if語句中,每當使用一個if,就會進行一次判斷,在多分支結構中,判斷次數有可能不止一次;
- switch語句中,判斷對象是表達式,起到判斷作用的是常量表達式,如果該表達式與某個常數表達式相等就進行跳轉,else if的判斷對象也是表達式,沒有利用常量表達式進行跳轉的功能;
- 單個switch語句就能實現多分支結構,但如果只有一個if else,最多只能實現二分支結構,必須多個else if語句配合;
- switch語句需要配合break語句實現多分支結構,否則有可能出現多個分支都被執行的情況,合理運用這個特點可以作為一種技巧,而else-if的判斷則是非此即彼的。
7.關於實現四舍五入的討論
在完成PTA平台上的習題時,我們會遇到題目要求實現四舍五入的情況,這個時候有兩種方法來實現。
法一:調用數學函數round(x)
應用舉例:
運行結果:
根據我這段簡單的代碼和調試結果,我們可以看出數學函數“round( )”的用法:當變量是浮點型時,該函數將返回小數對整數部分的四舍五入的值。
法二:利用寬度限制詞
應用舉例:
運行結果:
根據這段代碼以及運行結果,我們可以看出,在限制浮點數輸出的位數時,會自動實現四舍五入。
用強制類型轉換實現四舍五入的思考
想法來源:
讓我們來驗證一下曉凇學長提出的想法吧!
運行結果:
很遺憾,目前使用強制類型轉換實現四舍五入的方法“破產”了,但是,如果我非要用這個方式來實現四舍五入的話,能不能強行實現呢?
強行實現代碼:
運行結果:
我們成功地強行實現了四舍五入,你看懂這段代碼了嗎?我們把一個浮點數變量乘上了10,然后加上了5,再把它除以10,此時int類型的num2和num4會將結果的小數部分舍去,這時,如果小數部分是0.5~0.9的數字一定會進位,而小數部分是0.1~0.4的數字即使乘上10,加上5,也一定不會進位,當舍去小數部分時,除非進位了,否則我們加上的5和原有的小數部分都會被舍去,這樣就強行用強制類型轉換實現了四舍五入。
- 這只是一個想法,我們有更好的方法來實現四舍五入,因此這個想法只作為一個分享。
- 強制類型轉換的代碼格式是:(類型)<值>;在翁愷老師的MOOC中有對這個知識點的詳細介紹,因此不展開詳細敘述。
2.2 本章學習體會
學習感受
通過了這兩周的學習,我深刻理解到了老師說的“C語言不可能看會、聽會,只能練會!”在我看MOOC和聽林老師的講解時,有時候會覺得我已經能完全理解老師們講授的知識點,但是上機寫代碼時還是會漏洞百出。由此可見,想要學好C語言,投入時間,努力練習是必不可少的,必須有足夠多的代碼量,才能有一個扎實的編程基礎,這一定是需要一個過程的。在這個過程中,將會遇到各種奇形怪狀的問題,碰到很多Bug和我們躲貓貓,有時候明明實現的題目的要求,但還是有很多測試點無法順利通過,不得不花一整天甚至好幾天進行調試。但是,當把一段代碼好不容易熬熬熬熬出來之后,那種滿足感是多么的強烈啊!這兩周只是一個開始,希望在接下來的學習,我能夠擁有更濃烈的求知欲去鍛煉編寫代碼的能力。
教學建議
希望在接下來的課程中,能夠接觸到更多諸如隨機數的產生等拓展知識,增加知識儲備。
代碼量統計
- 僅記錄PTA答案正確的代碼量,不包括修改的代碼以及單行的“{ }”。
3.PTA實驗作業
3.1 判斷一個三位數是否為水仙花數
3.1.1數據處理
- 數據表達:
- int number1,存儲輸入的三位整數;
- int number2,備份number1,為判斷是否是水仙花數做准備;
- int position = 0,存放number1的各個位的數字;
- int i = 0,控制循環開始、結束的變量;
- int sum = 0,進行記錄水仙花數計算的結果,用於與number2比較。
- 數據處理:
一個輸入語句,一個for循環結構,兩個if-else二分支結構,三個輸出出口,具體流程在代碼截圖中有詳細說明。
3.1.2 代碼截圖
3.1.3 PTA提交列表及說明
Q:第一次提交時,由於for循環語句中的“循環進行的條件”語句沒有書寫正確,導致運行超時;
A:將該行代碼修改為“for (i = 0; i < 3; i++)“就可以順利結束循環。
3.1.4 本題可擴展功能
功能一:實現判斷一個任意位數的整數是否是水仙花數功能的函數,題目連接2840大題庫 實驗5-9 使用函數輸出水仙花數
思考:如果我們需要判斷任意位數的整數是否是水仙花數,代碼該怎么實現?
代碼實現:
功能二:找出n位數中的所有水仙花數,題目連接2840大題庫 實驗4-2-5-嵌套循環 水仙花數
思考:如果我們需要一次性找出n位數中的所有水仙花數,代碼該怎么實現?
代碼實現:
但是,如果直接將這段代碼提交了,我們會看到這樣的審核結果:
為什么會出現運行超時的情況呢?這就是林老師所說的數學函數pow(x,n)的運行效率問題,由於函數pow(x,n)內部是通過循環實現的,當多個循環嵌套時,運行時間將被大幅度延長。這個問題的解法是——我們自己寫一個mypow(x,n)函數:
然后用我們自己的函數替換掉所有的pow(x,n)函數:
我們可以很明顯地看到,程序運行效率被大幅度地提升了。
3.2 計算天數
3.2.1數據處理
- 數據表達:
- int year,存儲輸入的年份;
- int month,存儲輸入的月份;
- int day,存儲輸入的日期;
- int sum = 0,用於存放總天數。
- 數據處理
一個輸入語句,一個switch多分支結構,一個嵌套的if判斷語句,一個輸出出口,具體流程在代碼截圖中有詳細說明。
3.2.2 代碼截圖
法一:
3.2.3 本題可擴展功能
我一共有4種代碼寫法來完成這道題目。
法二:switch多分支結構改進
思考:如果是方法一的代碼,我們需要提前算好每個月份的第一天是這一年的第幾天,這么寫代碼比較繁瑣、費時,我們能不能讓計算機幫我們做這項工作呢?
代碼實現:
你看懂這段代碼了嗎?這段代碼利用了switch語句的特性:如果沒有遇到break語句,程序會繼續執行下一個case。當程序在switch結構發生跳轉后,會自動將接下來的所有case統統執行一遍,這樣就實現了天數的自動累加。代碼的其他部分不需要做改動。
法三:else-if多分支結構
思考:我們可不可以用另一種多分支結構來寫這段代碼呢?
代碼實現:
只需要將法一的switch結構替換成這段else-if結構的代碼,也可以實現計算天數,從這段代碼中,我們不難看出else-if多分支結構的判斷次數很有可能多於switch結構的判斷次數。
法四:數組
如果你有提前看書或者看MOOC,你就會知道數組也很適合用來解決這道題目。
代碼實現:
利用數組,我們也可以實現類似方法二的效果,而且這種方法的代碼量是相對較少的。
3.2.4 PTA提交列表及說明
A:我把以上四種代碼寫法都進行了提交,這四種方法都能夠完成這道題目。
3.3 於龍遇見日期,又哭了!
3.3.1數據處理
- 數據表達:
- int date[3],用數組存放輸入的三個數字;
- int i1 = 0,控制循環開始、結束的變量;
- char c1, c2,存放三個數字中間的連接符號,確保正確讀入數據;
- int oneYear[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 },該數組的單元初始化數據為對應月份的天數,判斷日期的合法性時要用
- int year = 0,存儲代表年份的數字;
- int day = 0,存儲代表日期的數字;
- int month = 0,存儲代表月份的數字;
- int idx1 = 0,用於記錄date數組中最大值的下標;
- int idx2 = 0,在第二次遍歷中,配合循環結構完成數組單元的位移;
- int judge = 0,作為判斷日期合法性的flag。
- 數據處理:
一個輸入語句,兩個for循環結構,三個if-else嵌套判斷結構,三個輸出出口,具體流程在代碼截圖中有詳細說明。
3.3.2 代碼截圖
3.3.3 本題可擴展功能
由於這道題的代碼功能比較全面,因此這個部分我主要談談我對這道題的解析與看法。我猜,部分同學寫這道題,可能寫着寫着就和於龍一起哭了起來,因為無論怎么改代碼,總有那么幾個測試點無法通過。正如林老師所說,這道題的思路其實並不難想,我認為難點在於兩大方面:一、能否找出輸入數據的所有可能性,並寫出相應的代碼分別操作?二、能否實現諸如閏年判斷、日期合法性判斷等判斷機制?第一個方面,我舉個例子,如果我們一上來就把輸入的三個數按排序,那么這種可能性就有可能被忽略,這時無論你怎么改后面的代碼,一定會有某幾個測試點過不去,很多同學面臨的很可能就是這個問題,因此在寫這道題之前,我們一定要想清楚,有多少種可能性是需要我們考慮的?我們的每一部分代碼能否對這些可能性進行相對應的操作?第二個方面,如果我們把閏年判斷、日期合法性判斷等機制拿出來單獨出題,也可以出幾道基礎題來,把思路逆轉過來,這道題也可以當作好幾道基礎題的拼接整合,在這個時候,代碼各個部分的協調顯得尤為重要,這個方面在我們寫到“圓形體體積計算器”這種題目,甚至是鏈表,就會更加體現不同部分代碼的協調問題。但是,如果這些拆分出來的小部分你無法順利完成的話,那么你可能就需要思考:我對知識點是不是不是很熟悉?代碼量是不是不太夠啊?如果是這樣的話,我們還有一個2840大題庫可以拿來進行練習。我還有一個想法,如果在出題時,老師把題干的7組數據刪掉,這道題的難度還能再提升一個檔次。除非能一次過關,否則我們就需要進行調試來尋找錯誤,在測試數據有限的情況下,我們能否自己造成一些特例去把那些在“躲貓貓”的可能性都找出來?我認為,這是我們應該要有意識去培養的一種能力。
3.3.4 PTA提交列表及說明
A:在小測結束后,我對我的代碼進行了優化,刪除了一些不必要的變量,簡化了代碼。
4.閱讀代碼
題目來源 zoj 1205 Martian Addition
題干:
題意概括:利用代碼模擬實現100位數字以內的20進制加法。
亮點分析:
- 這段代碼的作者通過字符串來解答這道題目,將題目分解成5個函數來解決問題,分別是int CharToVal(char c)和void CopyNum(char* dest, const char* line)兩個函數實現字符串的讀入,函數void Add( )實現20進制的加法,函數void PrintResult( )實現結果的輸出以及主函數int main( ),通過對題目的分解,使題目的功能更容易實現,也利於讀者閱讀學習;
- 使用了函數memset( ),高效地實現了對數組進行填充、清零的操作;
- 靈活運用字符串查找函數strchr( )和字符串長度函數strlen( ),配合for循環完成了字符串的准確輸入,同時在void CopyNum(char* dest, const char* line)函數中使用了“const char* line”的寫法,保證了指針line不能指向其他變量,保證了指針line所指向的變量的准確提取;
- 在void Add( )函數中,看似遙不可及的20進制運算,作者利用了for循環語句和求余運算實現了加法運算、進位的功能,達成了20進制加法的目的;
- 在int main( )主函數中,while的條件“scanf("%s",line)!=EOF && strcmp(line,"END")!=0”在讀入字符串的同時一並檢查是否開始、繼續循環,我是第一次看到這種寫法,不僅輸入了數據,更是直接參與了條件運算;
- 在void PrintResult( )輸出函數中,作者沒有直接輸出,而是利用數組的嵌套,實現了對照20進制數位表NUMS直接打印結果的功能。