*本文是CS50x的中文學習筆記,是原課程的筆記補充,建議先上完課,看完原課程筆記,再閱讀本文。
![]() |
| 這就是我的寫照,只有更誇張 |
不得不說上次的作業實在有點難度,讓我卡了兩天左右。不過有難度才有實踐的價值,所以繼續努力前進吧!
然後還是再說一句,這裡的文章,肯定會有一些知識我沒有補充到,甚至我在轉化知識時會有一些小錯誤,請搭配原文,並且勤加Google查詢。如果你有發現錯誤,歡迎留言指正哦!
編碼 compiling
C語言在電腦的執行過程,步驟分為:
- 前置處理,找出#include<XXX.h>
- 編碼成組合語言assembly code
- 將組合語言assembly code換成2進位的機器碼machine code
- 將編碼完成的工具箱跟我們已經編碼好的機器碼連結起來,產生一個2進位檔案a.out,或者任何你想取的名字。
CS50x寫了許多程式可以幫助學生學習程式語言。寫程式語言一定會產生BUG,也就是程式錯誤的地方。
我最常出現的錯誤就是這種類型的,編碼時就跳出紅字,透過help50很快能找到。例如以下範例:
![]() |
| 在命令列輸入help50 make filename,上述是敘述發現找不到string,因為缺乏cs50.h |
另外一個程式錯誤是邏輯錯誤,也就是說程式編碼正確,但是卻沒有產出你想要的結果。請見以下範例。
假設我想列印10個#,上述會跑出幾個?答案是跑出11個#。為什麼?因為是從0數到10,當然會跑出11個#囉。可是,如果今天程式複雜一點,會那麼容易找到答案嗎?另外,help50這支程式,可以幫我找到答案嗎?
可惜不行,因為程式正常運作,只是沒跑出我們預期的答案而已。
遇到這種情況,我自己是都用printf函數,一個一個把值印出來,我就會知道問題出在哪裡。例如在上次作業中,我就發現原本設了一個整數int的變數,但是透過迴圈運算,值越來越大,最後超過int給予的記憶體容量。如果發生這種事,沒印出來也不會知道。
不過,cs50x使用了更好的方法,叫做debug50,不過這個程式只能在CS50 IDE使用,無法用沙盒。我自己是還沒試用過,因為我覺得printf就很好用了。看影片介紹感覺也像是printf,只是比較慢一點,你可以看到程式目前執行到哪裡。
另外他們還寫了check50以及style50,一個是檢查程式是否能正確運作,一個是檢查程式的書寫方式是否符合習慣用法。如果有乖乖寫作業的話,相信對這兩個程式都不陌生。令人驚訝的是教授在課堂指出,在實際應用場合,的確會寫出如check50這種測試程式,來驗證程式是否正確,例如給程式資料,看會不會跑出想要的值。
資料型態Data Types
如果比較Python及C語言,我覺得最大的不同就是Python不用先規範資料型態,也就是說你不用跟電腦說你需要布林值,還是整數,亦或是浮點數,你就直接寫就是了。如下:
看了底下的C語言範例,你會發現Python已經把很多事情都幫你做好了,但是C語言什麼都要自己來。
C語言是在1970年代初期,在貝爾實驗室設計出來的。Python則是到了1990年才被設計出來,兩者相差了20年。所以我猜C語言當初這麼設計,就是因為當時電腦的記憶體容量不足,所以才會要先宣告資料型態,畢竟每個資料型態所佔的記憶體不同。
雖然說目前記憶體的容量都挺大的,但是我認為在設計程式時,還是得注意一下避免讓資料過於龐大,佔用了過多的記憶體。
陣列Array
這是典型的陣列,這裡先宣告陣列裡面有幾個值,這裡有3個值,所以要寫3。
陣列裡面會儲存值以及資料位置,所以在變數scores[i]裡面輸入數字,就會得到相對應的值。

當然你也可以寫這樣:
int scores[] = {72, 73, 33};
C語言會知道你裡面要放3個值,就不用特別給一個3。
陣列也變成表格一樣的方式,例如:
int array [2][3];

此外array還有一個特性,就是你可以直接更改array裡面的值。如果是變數,則不能更改變數的值。
這裡是課程範例介紹
![]() |
| a完全不動,b[0]則被改為42 |
為什麼會這樣呢,因為set_int(int x)宣告了x為整數,但是當我們使用set_int(a),只是把a複製一份過去,並沒有實際更改到a的值。
如果我們就是要改呢?那就做一點小修改即可,請看下列範例。

噠啦,兩個值都變成42了。
變數範圍Scope
程式寫到現在,大家應該會發現,雖然可以為變數命名,看你要叫fool還是stupid都可以,但是也不是想叫什麼就叫什麼。你總不能宣告整數int,還給它取名int。
所以這裡就該來介紹變數範圍了。
全域變數(Global variable)
就像是你不能將變數命名為int、bool、printf,你如果有些變數不想要別人修改,就該把他放在主程式以外,作為全域變數。
區域變數(Local variable)
通常是指在主程式或函式裡面設定的變數,只會存在於該主程式或函式裡,當程式結束,變數就會消失。
區塊變數(Block variable)
這個通常是指在迴圈裡,例如我們可能在for迴圈裡這樣寫
for (int i = 0; i > n; i++)
這裡寫 i 是一種常用寫法,變數取什麼都可以,但是我們通常懶得想,然後就取名叫 i,或者 x。
想想看一個程式裡可能會有好幾十個甚至上百個迴圈,每一個都要取新名還得了。
所以我們可能在迴圈A命名一個變數為i,另一個迴圈B裡,也命名一個變數為i,兩者之間不會互相干擾。不過迴圈裡的迴圈,還是要取不同名稱哦!
接下來我們來藉由一個簡單的圓面積計算程式來瞭解上述概念。
函式(function)
另外要藉著陣列的介紹,討論一下函式(function)。如以下範例:
字串Strings
談完陣列,接著講strings就會很好理解。strings就是複合的字元char,例如範例中的"EMMA",是由E、M、M、A,一共4個字元組合而成。
*後來做了作業後,發現char陣列比我想的還複雜,具有pointer的概念。留到之後再補充。
所以我們如果定義一個string name如下:
![]() |
| 其實這裡後面應該還有一個name[4],儲存\0,作為結尾。 |

答案揭曉:
- E = 69
- M = 77
- A = 65
所以這告訴了我們電腦的最基本原理,計算數字。大多數主流的程式語言都是以英文為主,但是中文也能當成程式語言,只要建立標準,寫好編譯器就好。電腦看不懂文字,它只在乎你輸入什麼數字進去而已。所以電腦才又叫做計算機。
接著我們運用這個概念,就能夠把小寫字母換成大寫,或者反過來。
原課程設計了一個能夠把小寫變成大寫的程式,這裡我稍微修改一下,如果輸入是大寫,就會變成小寫,如果輸入小寫,就會改為大寫。
就如同前面所述,每一個字元都代表一組數字,小寫的英文字母,介於97~122之間。大寫的英文字母,介於65~90之間。ASCII對照表如下。

在主程式內放入參數arguments
一直以來我們已經習慣在主程式,寫下int main(void),void是指程式內不放入參數,那如果我們要放入參數呢。
我們前面在設定average函式時,有放入參數,是這樣的式子:
float average(int length, int array[])
這邊的參數要怎麼設定都可以,不過如果是在主程式的參數,通常都是下面這樣。
int main(int argc, string argv[])
第一個參數int argc,代表在陣列裡有多少string要丟入,不用給它數字,它會自己算
第二個參數string argv[],當然就是陣列了。
主程式的參數,會在終端機的命令列提示符上輸入,底下是課程範例,我把這個程式取名叫command.c
![]() |
| 如果我們在命令列沒有輸入參數,就會印出hello, world |
![]() |
| 在./command後面,輸入我的名字,就會顯示hello, Kai |
注意我們在這裡輸入的是./command Kai,./command在第一位,所以就是argv[0],Kai的位置在argv[1]。
也因此在程式裡才會寫printf("hello, %s\n", argv[1]);,因為我們只在乎輸入的名字。
那奇怪了,為什麼程式要問if (argc == 2),為什麼是2?
這裡會是2,是因為我們一共丟入兩個string:

我這裡修改了原程式範例,會更好理解

![]() |
| 這裡輸入./command Kai Yuan,一共丟入3個string,分別是位在argv[0]、argv[1]、argv[2] |
為什麼主程式是int main()
另外在上次提到,主程式int main會返回整數給系統,所以才要用int開頭。這裡解釋了這個問題,用int開頭是因為要返回整數,才知道程式有無出錯。如果retrun 0;,代表沒問題,反之若retrun 1;,就是出現問題。預設會是return 0;。

等等,明明在布林值裡,0為假、錯誤,1為真、正確,感覺就是要回傳1,才代表正常啊,怎麼這裡變成retrun 0;沒問題。
嗯...該怎麼解釋呢,其實就是一開始電腦專家決定好,返回0代表沒問題。另外,這裡的返回不像是布林值,只有0與1,可以分別針對錯誤去設定數字,所以這裡的0,代表0錯誤的意思。
作業
這次有兩份作業,一個是閱讀能力分級,另一個是文字加密,同樣也有分難易度,我仍然是挑較難的來做。
作業的目的是為了測試你懂不懂得在C語言裡的Strings邏輯。
首先在閱讀能力分級作業中,你必須做到底下這些事:
- 在~/pset2/readability 資料夾裡,創一個readability.c檔案。
- 使用get_string,讓使用者輸入 string資料。
- 建議使用ctype.h工具箱裡面的tolower函式,將輸入的string資料通通變成小寫,這樣就不用煩惱大小寫問題。
- 設計3個函式去計算字母(letters)、單字(words)、句子(sentences)。因為你輸入的string資料是陣列,所以你可以用for迴圈計算字母,只要把目標限制在字母即可。計算句子也是同樣邏輯,差別是你僅需在乎句子結尾是句點、驚嘆號,或是問號。計算單字則會比較困難一點,同樣也是for迴圈,但是我在迴圈裡,又再加了一個while迴圈。在作業那邊有助教拍攝的教學影片,可參考。
得到字母、單字、句子數量後,就能夠計算Coleman-Liau指數,公式如下:
index = 0.0588 * L - 0.296 * S - 15.8
這裡的L是每100單字的平均字母數,S是每100單字的平均句子數。如果指數小於1,就印出"Before Grade 1",如果大於等於16,就印出 "Grade 16+",其他的部分就看指數多少就印多少,指數算出來通常是浮點數,所以記得印出來前要使用math.h的round函式,把浮點數變成整數。
另外我發現,C語言其實沒有string這個資料型態,是用char array[]模擬的。所以你如果要寫一個函式計算字數,你雖然是丟入string,但是你必須寫char array[],例如int count_letters(char array[]);,底下是我寫的作業,可供參考,但仍建議先自己想想怎麼寫。
作業範例
1. Readability
2. Substitution
(這題殺死我好多腦細胞啊嗚嗚)
Happy coding!







沒有留言:
張貼留言
謝絕廣告,感恩