【CS50x(2022)】 Week2 - Array
這週提供了遇到 bug 時的解決方式,也深入講解了 main function 的使用。
課程內容
- Compiling
- Debugging
- Memory
- Arrays
- Characters
- Strings
- Command-line arguments
- Applications
Debugging methods
寫程式常常遇到一個讓人困擾的問題,就是運作起來跟想像中的不一樣。大家常說的 debug 或者說除錯,其實就是找出程式運作不如預期的原因,並且解決它的過程。用肉眼直接找出原因是很沒有效率的,畢竟就是覺得邏輯沒問題才會這樣寫並且很有自信地讓程式碼運行 😅
講師介紹了 3 種課程中遇到 bug 可以使用的方法,除了 debug50 這個課程提供的工具外,其他兩種方法也是我在學習過程中很常使用的 debug 方式。
用 printf()
呈現程式運作過程
printf()
是一個在 terminal 印出()
中內容的指令。在程式碼適當位置中加入,可以觀察程式碼運作細節,方便藉由觀察印出的「值」跟預想中的相不相符來確認發生錯誤的地方。
例如:如果要在 terminal 印出3個 #
1 |
|
這個時候就可以在迴圈內加一行 printf()
來檢查輸出的次數為什麼和預期的不一樣。
1 |
|
這樣就可以看出 for loop 的迴圈檢查條件設置錯誤,只要將 i <= 3
更正為 i < 3
即可。
這種方式也是我目前最常使用的 debug 方式。當運行結果和預期中不同時,藉由這種方式可以把一大段的程式碼分區塊進行測試,針對特定段落進行檢查,可以更精確地找到發生問題的原因並進行改正。
debug50(課程工具)
CS50 課程開發了線上的 codespace ,裡面已經進行了相關的環境設定、也提供了一些工具,讓註冊這門課的學生可以專注在學習課程內容上。 debug50 就是其中一個好用的工具,可以把程式運作的流程一步一步的呈現,在複雜一點的程式結構 debug 上會比 printf()
更方便一點。
Rubber duck
Rubber duck debugging 是用書寫或是口說的方式,將整個流程一步一步的說出來。藉由這個「輸出」的過程來釐清每個步驟,進而找出發生錯誤的原因。
Rubber duck 對我來說不僅僅用在 debug 上,通常在正式開始寫程式前,我就會先用 pseudocode 的方式規劃整個流程,除錯過程中比對原來的 pseodocode 來進行檢查。另外就是,「寫部落格」對我來說也是 Rubber duck 的應用,要進行「輸出」首先要先在腦中進行整理,之後才能用比較有條理的方式進行表達。
Characters
每一個 Character 在記憶體中是以 ASCII value 的形式紀錄的,這也讓 char
這個 data type 有很特別的應用方式。
1 |
|
在 week1 的 C 語言介紹課程供提到,使用 printf()
引用變數時,必須清楚定義變數的 data type 。在前面課程中覺得「沒必要」、「多此一舉」的動作,在這裡就體現出了這麼做的原因。 由於是以 ASCII value 來紀錄,因此 data type 的定義也就關係到了記憶體中 value 的解讀方式,進而能呈現出不同的結果。
在大小寫的轉換中,也可以用很特別的寫法:
1 |
|
看起來很奇怪,「文字」怎麼能進行「比大小」和「四則運算」?其實就是 ASCII value 在不同型別間的轉換而已。
Array & String
Array
Array 是一種 data type ,它可以讓很多相同型別的變數值共用一個變數名稱,並且 array 裡面的值是有「順序性」的,這種方式可以使用較少的變數紀錄相同數量的值。
1 |
|
String
String 不是 C 語言原生的 data type ,它是由 CS50 library 定義的,所以如果想使用 String ,就要在文件開頭引用 <CS50.h>
。
Strings are actually just arrays of characters —— CS50x
這也是為什麼可以用 array 方式來取 string 裡第 i 個字的原因。
1 |
|
每個 string 結尾會被加上 null character
, or NUL
, or \0
,用來表示字串的結束。這也是為什麼可以用下列方式來計算字串的長度。
1 |
|
Command-line arguments & Exit status
這關係到 C 的主程式結構。在之前的課程介紹中,講者只說明主程式要寫在 int main(void){}
裡面,卻沒有特別說明原因。其實,可以把主程式看成一個 function 。在 week1 有介紹過 function ,其結構是:return_type function_name(Command-line arguments){}
,也就是我們最一開始看到的:
1 |
|
Command-line arguments
通常, main function 不需要引入任何參數就可以直接執行,所以我們直接使用 void
來表示。但既然是 funtion ,當然也有「引入參數」的使用方法,那就是直接在 Command-line 執行 ./file_name
的時候直接放入參數。
1 |
|
argc
代表的是「包含執行指令」的 arguments 的 length;而 argv
則是執行指令後設置的參數(其型別為 array of string)。
也就是說,如果我在 terminal 執行以下指令:
1 |
|
此時,我就可以在我的 main function 中使用我在 terminal 輸入的參數。
exit status
嗯嗯?主程式有 return value ?? 看看 int main(void){}
……看起來 return value type 是 interger ,但是我們並沒有設定 return value 啊?
其實, main function 預設會回傳 0
表示沒有任何錯誤發生;而我們可以設置不等於 0
的 return value 來表示錯誤情況發生。可以針對不同的錯誤狀況設置不同的 return value ,這也是我們在執行一些微軟或 Mac 程式,發生錯誤時看到的 error message number。
來看看例子。假如我寫了一個 main function 需要使用者提供一個參數(name),如果參數無誤會在 terminal 印出 Hello, name
:
1 |
|
小結
相較於前兩週,我花了較多的時間反覆觀看 lecture 影片,不但因為資訊量變大更需要時間吸收,也因為接觸更多沒碰過的「基礎」(像是資料在 memory 中的儲存方式及應用方法)。除了提升對 C 的理解外,也解決了一些我在學習 JavaScript 時的疑惑!
相關文章: CS50x(2022)
文章內容如有錯誤,歡迎留言討論!
本 Blog 上的所有文章除特别聲明外,均採用 CC BY-SA 4.0 協議 ,轉載請註明出處!